poj 2774 后缀数组(最长连续子串)

来源:互联网 发布:宽带通属于什么网络 编辑:程序博客网 时间:2024/05/16 17:58

题意:给定两个字符串,求这两个字符串的最长连续子串(注意和最长公共子串LCS的区别。LCS用dp来求)。

思路:后缀数组。有关后缀数组的概念可以参见罗穗骞的一篇文章。这道题的思路就是将两个字符串连接起来(中间用一个不相关的字符分开),再用后缀数组求出height数组的值,找出一个height值最大并且i与i-1的sa值分别在两串字符中就OK。

其中一个地方wa了n次,在最后判断的时候抖了个机灵写成(sa[i]-len1)*(sa[i-1]-len1)<0,原本以为其和(sa[i]<len1&&sa[i-1]>len1)||(sa[i]>len1&&sa[i-1]<len1)表达相同的意思。wa的原因应该和数据相乘溢出有关,因为字符串长度上界是200000,如果两个乘数都较大(比如都为50000),那么相乘溢出了int范围为负数,导致错误。

#include <stdio.h>#include <string.h>#define swap(a,b,c) c = a,a = b,b = c;#define N 200005char s[N];int top[N],rank[N],b[N],v[N],r[N],sa[N],rank[N],h[N];int cmp(int *q,int x,int y,int j){//非常巧妙。如果r[a]=r[b],说明以r[a]或r[b]开头的长度为l的字符串肯定不包括字符r[n-1],所以调用变量r[a+l]和r[b+l]不会导致数组下标越界    return (q[x]==q[y])&&(q[x+j]==q[y+j]);//表示当前比较的2j长的字符串中前j个相同,后j个也相同}void getsa(int n,int m){//m是计数排序中的字符个数    int i,j,p;    int *x = rank,*y = b,*t;    memset(top,0,sizeof(top));    for(i = 0;i<n;i++)        top[ x[i]=r[i] ]++;    for(i = 1;i<m;i++)        top[i] += top[i-1];    for(i = n-1;i>=0;i--)//对长度为1的字符串进行排序(结果相当于对原字符串进行排序,此时sa保存的是排名为下标的是第几个字符)        sa[--top[x[i]]] = i;        for(p = j = 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++)            v[i] = x[y[i]];        memset(top, 0, sizeof(top));        for(i = 0;i<n;i++)            top[v[i]]++;        for(i = 1;i<m;i++)            top[i] += top[i-1];        for(i = n-1;i>=0;i--)            sa[--top[v[i]]] = y[i];        swap(x,y,t);        x[sa[0]] = 0;        for(p = i = 1;i<n;i++)            x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1:p++;    }}void getlcp(int n){//longest common prefix    int i,j,k=0;    for(i = 1;i<=n;i++)        rank[sa[i]] = i;    for(i = 0;i<n;i++){        if(k)            k--;        j = sa[rank[i]-1];        while(r[i+k] == r[j+k])            k++;        h[rank[i]] = k;     }}int main(){    while(scanf("%s",s) != EOF){        int i,len1,len,res=0;        len1 = (int)strlen(s);        s[len1] = 'a'+26;        scanf("%s",s+len1+1);        len = (int)strlen(s);        for(i = 0;i<len;i++)            r[i] = s[i] - 'a' + 1;        r[len] = 0;        getsa(len+1,29);        getlcp(len);        for(i = 1;i<=len;i++)            if(h[i]>res && ((sa[i]<len1&&sa[i-1]>len1)||(sa[i]>len1&&sa[i-1]<len1)))                res = h[i];            /*if(h[i]>res && ((sa[i]-len1)*(sa[i-1]-len1)<0))                res = h[i];*/        printf("%d\n",res);    }    return 0;}


0 0
原创粉丝点击