[POI2000] 最长公共子串
不知道为什么很多人学完SA后的第一道题都是这个。
我寒假就写过这道题,而且SA只写了这道。
但是,但是,但是,我今天又写了一遍,当然是A掉了。
但是,我觉得手还比较生,准备在调整一下代码的格局。
于是,我开始写第二遍。
“好像不太对经啊。”
“就是不对劲啊。”
“怎么可能吗!”
我对我的板子产生了质疑,于是来了组 aaaa 的数据测试一下。
“。。。。。。”
所以我是怎么A掉这题的......
sa和rank都错了。
不可思议。
搞一下中间结果。
“奥,我知道了。”
在我原来的板子中,排名最小的后缀的排名是0,而对于长度不够没有第二关键字的后缀的第二关键字排名也是0。
导致在第二次基数排序中(len=2),后缀Sufix(1)=aaa,后缀sufix(2)=aa的第一关键字与第二关键字均相同的情况。
其中Sufix(1)的rank2由rank[1+2]=rank[3]=0得到,Sufix(2)的rank2由(i+l>=n)得到0,而显然这两个后缀是不同的的。
在上午剩下的时间里我都在改板子了,最后算是改成正确并且满意的板子。
细思极恐......
回到这道题,对于SA的题,一般将最值问题转化为判定性问题。
我们先将多个串接成一个串,中间用不同的字符连接,求一边SA。
之后我们二分最大值x,并借助Height数组判定。
首先,相同子串一定来自于一段连续的rank,也就是说要连续若干个Height值大于等于x。之后判断这连续若干个后缀中是否每个串的后缀都出现过。在拼串的时候记录一下每个位置的字符属于哪个串就可以了。
// q.c #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define mem(a) memset(a,0,sizeof(a)) using namespace std; const int M=2000*5+100; struct Suffix_Array { int n,m,h[M]; int sa[M],tmp[M],cnt[M]; int rank[M],rank1[M],rank2[M]; Suffix_Array():n(0),m(0) { mem(h); mem(sa); mem(tmp); mem(cnt); mem(rank); mem(rank1); mem(rank2); } bool same(int x,int y) { return rank1[sa[x]]==rank1[sa[y]]&&rank2[sa[x]]==rank2[sa[y]]?true:false; } void get_SA(char *s) { mem(cnt); for(int i=1;i<=n;i++) cnt[s[i]-'a']++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) rank[i]=cnt[s[i]-'a']; for(int l=1;l<=n;l<<=1) { for(int i=1;i<=n;i++) { rank1[i]=rank[i]; rank2[i]=(i+l<=n)?rank[i+l]:0; } mem(cnt); for(int i=1;i<=n;i++) cnt[rank2[i]]++; for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) tmp[cnt[rank2[i]]--]=i; mem(cnt); for(int i=1;i<=n;i++) cnt[rank1[i]]++; for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[rank1[tmp[i]]]--]=tmp[i]; rank[sa[1]]=1; for(int i=2;i<=n;i++) rank[sa[i]]=same(i,i-1)?rank[sa[i-1]]:rank[sa[i-1]]+1; if(rank[sa[n]]==n) break; } } void get_HT(char *s) { for(int i=1,j=0,L=0;i<=n;i++) if(rank[i]>1) { j=sa[rank[i]-1]; while(i+L<=n&&j+L<=n&&s[i+L]==s[j+L]) ++L; h[rank[i]]=L; if(L) --L; } } void prepare(char *s,int x,int y,int a[],int b[]) { n=x; m=y; get_SA(s); get_HT(s); memcpy(a,sa,sizeof(sa)); memcpy(b,h,sizeof(h)); } }SA; char s[M]; int n,len,pos[M],H[M],Sa[M]; bool vis[M]; bool check(int x) { for(int i=1,j=0,sum=0;i<=len;i=j+1,sum=0) { memset(vis,0,sizeof(vis)); for(j=i;j<=len&&H[j]>=x;j++) if(pos[Sa[j-1]]&&pos[Sa[j]]&&pos[Sa[j-1]]!=pos[Sa[j]]) { if(!vis[pos[Sa[j-1]]]) vis[pos[Sa[j-1]]]=true,++sum; if(!vis[pos[Sa[j]]]) vis[pos[Sa[j]]]=true,++sum; if(sum>=n) return true; } } return false; } int main() { freopen("pow.in","r",stdin); freopen("pow.out","w",stdout); int l=1,mid=0,r=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",s+l); r=strlen(s+1); for(int j=l;j<=r;j++) pos[j]=i; s[++r]='z'+i,l=++r; } SA.prepare(s,len=--r,30,Sa,H); for(l=0,mid=(l+r)>>1;l<=r;mid=(l+r)>>1) check(mid)?l=mid+1:r=mid-1; printf("%d\n",l-1); return 0; }