SPOJ 1812 LCS2 [后缀自动机 DP]
题意:
求多个串<=10的最长连续子串
一个串建SAM,然后其他串在上面走
每个状态记录所有串在这个状态的公共子串的最小值
一个串在上面走的时候记录与每个状态公共子串的最大值,注意出现次数向父亲传递,一个状态能到达说明了Suffix Link指向的状态可以取到最大子串,这一步对val后基数排序然后倒着更新就行了
注意两点:
1.空间要开两倍,基数排序用的东西也要两倍哦
2.答案不能用mn[1]更新!!!!因为mn[1]没有意义啊root状态一个子串也没有还可能有某些bug
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=2e5+5; int n,m; char s[N]; struct State{ int ch[26],par,val; }t[N<<1]; int sz,root,last; inline int nw(int _){t[++sz].val=_;return sz;} void iniSAM(){sz=0;root=last=nw(0);} void extend(int c){ int p=last,np=nw(t[p].val+1); while(p&&t[p].ch[c]==0) t[p].ch[c]=np,p=t[p].par; if(p==0) t[np].par=root; else{ int q=t[p].ch[c]; if(t[q].val==t[p].val+1) t[np].par=q; else{ int nq=nw(t[p].val+1); memcpy(t[nq].ch,t[q].ch,sizeof(t[q].ch)); t[nq].par=t[q].par; t[q].par=t[np].par=nq; while(p&&t[p].ch[c]==q) t[p].ch[c]=nq,p=t[p].par; } } last=np; } int mx[N<<1],mn[N<<1]; void walk(char s[]){ int sum=0,u=root,n=strlen(s+1); for(int i=1;i<=n;i++){ int c=s[i]-'a'; if(t[u].ch[c]) u=t[u].ch[c],mx[u]=max(mx[u],++sum); else{ while(u&&!t[u].ch[c]) u=t[u].par; if(!u) u=root,sum=0; else sum=t[u].val,u=t[u].ch[c],mx[u]=max(mx[u],++sum); } } } int c[N<<1],a[N<<1],ans; void RadixSort(int len){ for(int i=1;i<=sz;i++) c[t[i].val]++; for(int i=1;i<=len;i++) c[i]+=c[i-1]; for(int i=sz;i>=1;i--) a[c[t[i].val]--]=i; for(int i=1;i<=sz;i++) mn[i]=t[i].val; } void solve(){ iniSAM(); scanf("%s",s+1); int len=strlen(s+1); for(int i=1;i<=len;i++) extend(s[i]-'a'); RadixSort(len); while(scanf("%s",s+1)!=EOF){ walk(s); for(int j=sz;j>=1;j--){ int u=a[j]; mn[u]=min(mn[u],mx[u]); if(mx[u]&&t[u].par) mx[t[u].par]=t[t[u].par].val; mx[u]=0; } } for(int i=2;i<=sz;i++) ans=max(ans,mn[i]); printf("%d",ans); } int main(){ //freopen("in","r",stdin); solve(); }
Copyright:http://www.cnblogs.com/candy99/