spoj LCS2 - Longest Common Substring II && LCS - Longest Common Substring

多串LCS很适合SA但是我要学SAM
对第一个串求SAM,然后把剩下的串在SAM上跑,也就是维护p和len,到一个点,如果有ch[p][c],就p=ch[p][c],len++,否则向fa找最下的有c[p][c]的p,然后len=dis[p]+1,p=ch[p][c],否则就p=root,len=0(这个len每到一个节点就更新这个节点的f)
然后注意到在parent树上,因为每个节点代表的right集合是儿子的并集,所以f[u]是可以更新f[fa[u]]的,所以从底向上更新一遍(注意先更新!!)
最终点u的f就是每个串的f与dis[u]取min,然后ans在这些点上取max




cpp #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=500005; int n,m=1,fa[N],ch[N][27],dis[N],cur=1,con=1,la,f[N][15],c[N],a[N],ans; char s[N]; void ins(int c,int id) { la=cur,dis[cur=++con]=id; int p=la; for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur; if(!p) fa[cur]=1; else { int q=ch[p][c]; if(dis[q]==dis[p]+1) fa[cur]=q; else { int nq=++con; dis[nq]=dis[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q]; fa[q]=fa[cur]=nq; for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } } int main() { scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++) ins(s[i]-'a',i); for(int i=1;i<=con;i++) f[i][1]=dis[i]; while(~scanf("%s",s+1)) { n=strlen(s+1),m++; for(int i=1,p=1,len=0;i<=n;i++) { int c=s[i]-'a'; if(ch[p][c]) len++,p=ch[p][c],f[p][m]=max(f[p][m],len); else { for(;p&&!ch[p][c];p=fa[p]); if(!p) p=1,len=0; else len=dis[p]+1,p=ch[p][c],f[p][m]=max(f[p][m],len); } } } for(int i=1;i<=con;i++) c[dis[i]]++; for(int i=1;i<=con;i++) c[i]+=c[i-1]; for(int i=1;i<=con;i++) a[c[dis[i]]--]=i; for(int j=2;j<=m;j++) for(int i=con;i>=1;i--) f[fa[a[i]]][j]=max(f[fa[a[i]]][j],f[a[i]][j]); for(int i=1;i<=con;i++) { for(int j=2;j<=m;j++) f[i][1]=min(f[i][1],f[i][j]); ans=max(ans,f[i][1]); } printf("%d\n",ans); return 0; }

猜你喜欢

转载自www.cnblogs.com/lokiii/p/10005663.html