[BZOJ4698][SDOI2008]Sandy的卡片(后缀自动机)
差分之后就是求多串LCS。
对其中一个串建SAM,然后把其它串放在上面跑。
对SAM上的每个状态都用f[x]记录这个状态与当前串的最长匹配长度,res[x]是对每次的f[x]取最小值。答案就是res[]的最大值。
考虑f[x]的求法,把s[]放在SAM上跑时,若下一个能正常匹配(即son[x][c]!=0)则直接len++,否则用经典的跳父亲,找到第一个son[k][c]!=0的点k,len=mx[k]+1,x=son[k][c]。
每次更新最终匹配到的状态的f[]。同时注意到出现次数可以向父亲传递,于是Radixsort之后经典DP转移最长长度即可。
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=500010; 10 int n,m,x,len,fir,lst=1,cnt=1,ans,res[N],s[N],f[N],mx[N],fa[N],c[N],q[N]; 11 map<int,int>son[N]; 12 13 void ext(int c){ 14 int p=lst,np=lst=++cnt; mx[np]=mx[p]+1; 15 while (p && !son[p][c]) son[p][c]=np,p=fa[p]; 16 if (!p) fa[np]=1; 17 else{ 18 int q=son[p][c]; 19 if (mx[q]==mx[p]+1) fa[np]=q; 20 else{ 21 int nq=++cnt; mx[nq]=mx[p]+1; son[nq]=son[q]; 22 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p]; 23 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 24 } 25 } 26 } 27 28 void Radix(){ 29 rep(i,1,cnt) c[mx[i]]++; 30 rep(i,1,cnt) c[i]+=c[i-1]; 31 for (int i=cnt; i; i--) q[c[mx[i]]--]=i; 32 rep(i,1,cnt) res[i]=mx[i]; 33 } 34 35 void Go(int s[],int n){ 36 int x=1,len=0; 37 rep(i,1,n){ 38 int c=s[i]; 39 if (son[x][c]) x=son[x][c],f[x]=max(f[x],++len); 40 else{ 41 while (x && !son[x][c]) x=fa[x]; 42 if (!x) x=1,len=0; else len=mx[x]+1,x=son[x][c],f[x]=max(f[x],len); 43 } 44 } 45 for (int i=cnt; i; i--){ 46 int x=q[i]; res[x]=min(res[x],f[x]); 47 if (f[x] && fa[x]) f[fa[x]]=mx[fa[x]]; 48 f[x]=0; 49 } 50 } 51 52 int main(){ 53 scanf("%d%d%d",&n,&len,&fir); 54 rep(i,1,len-1) scanf("%d",&x),s[i]=x-fir,fir=x; 55 len--; rep(i,1,len) ext(s[i]); 56 Radix(); 57 rep(i,2,n){ 58 scanf("%d%d",&len,&fir); 59 rep(i,1,len-1) scanf("%d",&x),s[i]=x-fir,fir=x; 60 len--; Go(s,len); 61 } 62 rep(i,2,cnt) ans=max(ans,res[i]); 63 printf("%d\n",ans+1); 64 return 0; 65 }