[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转移最长长度即可。

 #include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,m,x,len,fir,lst=,cnt=,ans,res[N],s[N],f[N],mx[N],fa[N],c[N],q[N];
map<int,int>son[N]; void ext(int c){
int p=lst,np=lst=++cnt; mx[np]=mx[p]+;
while (p && !son[p][c]) son[p][c]=np,p=fa[p];
if (!p) fa[np]=;
else{
int q=son[p][c];
if (mx[q]==mx[p]+) fa[np]=q;
else{
int nq=++cnt; mx[nq]=mx[p]+; son[nq]=son[q];
while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
}
}
} void Radix(){
rep(i,,cnt) c[mx[i]]++;
rep(i,,cnt) c[i]+=c[i-];
for (int i=cnt; i; i--) q[c[mx[i]]--]=i;
rep(i,,cnt) res[i]=mx[i];
} void Go(int s[],int n){
int x=,len=;
rep(i,,n){
int c=s[i];
if (son[x][c]) x=son[x][c],f[x]=max(f[x],++len);
else{
while (x && !son[x][c]) x=fa[x];
if (!x) x=,len=; else len=mx[x]+,x=son[x][c],f[x]=max(f[x],len);
}
}
for (int i=cnt; i; i--){
int x=q[i]; res[x]=min(res[x],f[x]);
if (f[x] && fa[x]) f[fa[x]]=mx[fa[x]];
f[x]=;
}
} int main(){
scanf("%d%d%d",&n,&len,&fir);
rep(i,,len-) scanf("%d",&x),s[i]=x-fir,fir=x;
len--; rep(i,,len) ext(s[i]);
Radix();
rep(i,,n){
scanf("%d%d",&len,&fir);
rep(i,,len-) scanf("%d",&x),s[i]=x-fir,fir=x;
len--; Go(s,len);
}
rep(i,,cnt) ans=max(ans,res[i]);
printf("%d\n",ans+);
return ;
}
上一篇:[bzoj4698][Sdoi2008]Sandy的卡片_后缀数组_二分/单调队列_双指针


下一篇:【BZOJ4698】[SDOI2008]Sandy的卡片