poj(2774)后缀数组法求公共连续…

来源:互联网 发布:linux远程桌面工具 编辑:程序博客网 时间:2024/06/04 19:08
题目原意:输入两个字符串,求最长的连续公共子串的长度;
看了好长时间时间这个题,要用到后缀数组,于是下了个模板基数排序求后缀数组AC的。将两个字符串连接起来(模板里的基数排序求后缀数组要用int型,so将每个字符转换成整形),为了使得出现最长的公共子串在前后两部分里面,所以中间加个不可能出现的值(随意);然后将求最长公共子串(同时要判断的是公共子串的开头是否在两个区域OK了)。


#include<iostream>
#include<string>
using namespace std;
const int MAX = 200100;

int n, num[MAX];
int sa[MAX], rank[MAX],height[MAX];//sa[i]表示排名第i的后缀的位置,height[i]表示后缀SA[i]和SA[i-1]的最长公共前缀
int wa[MAX], wb[MAX], wv[MAX], wd[MAX];//基数排序模板不要修改

int cmp(int *r, int a, int b, int l)
{
    return r[a] == r[b]&& r[a+l] == r[b+l];
}

void da(int *r, int n, int m)          // 倍增算法0(nlgn)。
{
    int i, j, p, *x = wa, *y= wb, *t;
    for(i = 0; i< m; i ++) wd[i] = 0;
    for(i = 0; i< n; i ++) wd[x[i]=r[i]] ++;
    for(i = 1; i< m; i ++) wd[i] += wd[i-1];
    for(i = n-1; i>= 0; i --) sa[-- wd[x[i]]] = i;
    for(j = 1, p = 1; p< n; j *= 2, m = p)
    {
       for(p = 0, i = n-j; i < n; i ++)y[p ++] = i;
       for(i = 0; i < n; i ++) if(sa[i]>= j) y[p ++] = sa[i] - j;
       for(i = 0; i < n; i ++) wv[i] =x[y[i]];
       for(i = 0; i < m; i ++) wd[i] =0;
       for(i = 0; i < n; i ++) wd[wv[i]]++;
       for(i = 1; i < m; i ++) wd[i] +=wd[i-1];
       for(i = n-1; i >= 0; i --) sa[--wd[wv[i]]] = y[i];
       for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i= 1; i < n; i ++)
       {
           x[sa[i]] =cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
       }
    }
}

void calHeight(int *r, int n)          // 求height数组。
{
    int i, j, k = 0;
    for(i = 1; i<= n; i ++) rank[sa[i]] = i;
    for(i = 0; i< n; height[rank[i ++]] = k)
    {
       for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] ==r[j+k]; k ++);
    }
}

int main()
{
    int i=0, m;
    string s1;
    string s2;
   cin>>s1>>s2;
    for(intj=0;j<s1.size();j++)
    {
       num[i++]=s1[j]-'a'+10;
    }
    num[i++]=1;
    for(intj=0;j<s2.size();j++)
    {
       num[i++]=s2[j]-'a'+10;
    }
    m = 40;
    //在num[]中(结尾或中间)增加分隔符。*:需开始时就清晰da(num, n, m)之后的sa[]数组。
    n=i-1;
    //num[]每个元素都小于m。*:n+1为加了1个分隔符后的总长度。
    da(num, n + 1, m);
    calHeight(num, n);
    int ans=0;
    for(inti=2;i<=n;i++)
    {
       if(height[i]>ans)
       {
          if((sa[i]<s1.size()&&sa[i-1]>s1.size())||(sa[i-1]<s1.size()&&sa[i]>s1.size()))
          ans=height[i];
       }
    }
   cout<<ans<<endl;
    return 0;
}

0 0