不知道为什么很多人学完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;
}