题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032
序列自动机其实就是每个位置记录一下某字母后面第一个出现位置,为了子序列能尽量长。
对字符串B建一个后缀自动机,一个序列自动机,然后让A在上面找即可;
1.枚举A每个位置开始的子串,在SAM上走,失配就找到了;
2.枚举A子串,用B的序列自动机找失配处;
3.设 f[i][j] 表示A串前 i 个字符形成的子序列对应SAM上节点 j 时的最短子序列长度,可以转移;
4.f[i][j] 表示A串前 i 个字符的子序列对应B串第 j 个位置时的最短子序列长度,用序列自动机转移;
所以注意 f[i][j] 的第二维要开 4000!否则不是 MLE 而是 WA 。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=2005,inf=0x3f3f3f3f; int fa[xn<<1],l[xn<<1],go[xn<<1][30],lst=1,cnt=1,f[xn][xn<<1],g[xn][30],nxt[30];//<<1!! char s1[xn],s2[xn]; void add(int w) { int p=lst,np=++cnt; lst=np; l[np]=l[p]+1; for(;p&&!go[p][w];p=fa[p])go[p][w]=np; if(!p)fa[np]=1; else { int q=go[p][w]; if(l[q]==l[p]+1)fa[np]=q; else { int nq=++cnt; l[nq]=l[p]+1; memcpy(go[nq],go[q],sizeof go[q]); fa[nq]=fa[q]; fa[q]=fa[np]=nq; for(;go[p][w]==q;p=fa[p])go[p][w]=nq; } } } int main() { scanf("%s",s1+1); int l1=strlen(s1+1); scanf("%s",s2+1); int l2=strlen(s2+1); for(int i=1;i<=l1;i++)s1[i]-='a'; for(int i=1;i<=l2;i++)s2[i]-='a'; for(int i=1;i<=l2;i++)add(s2[i]); for(int i=0;i<26;i++)nxt[i]=l2+1; for(int i=l2;i>=0;i--) { for(int j=0;j<26;j++)g[i][j]=nxt[j]; nxt[s2[i]]=i; } int ans=inf; for(int i=1;i<=l1;i++) { int p=1,j; for(j=i;j<=l1;j++) { if(!go[p][s1[j]])break; p=go[p][s1[j]]; } if(j<=l1)ans=min(ans,j-i+1); } if(ans==inf)ans=-1; printf("%d ",ans); ans=inf; for(int i=1;i<=l1;i++) { if(g[0][s1[i]]>l2){ans=min(ans,1); continue;} int p=g[0][s1[i]],j; for(j=i+1;j<=l1;j++) { if(g[p][s1[j]]==l2+1)break; p=g[p][s1[j]]; } if(j<=l1)ans=min(ans,j-i+1); } if(ans==inf)ans=-1; printf("%d ",ans); memset(f,0x3f,sizeof f); f[0][1]=0; ans=inf; for(int i=0;i<l1;i++) for(int j=1,p;j<=cnt;j++) { if(f[i][j]==inf)continue; f[i+1][j]=min(f[i+1][j],f[i][j]); if((p=go[j][s1[i+1]]))f[i+1][p]=min(f[i+1][p],f[i][j]+1);//,printf("%d %d p=%d ",j,s1[i+1],p); else ans=min(ans,f[i][j]+1); } if(ans==inf)ans=-1; printf("%d ",ans); memset(f,0x3f,sizeof f); f[0][0]=0; ans=inf; for(int i=0;i<l1;i++) for(int j=0,p;j<=l2;j++) { if(f[i][j]==inf)continue; f[i+1][j]=min(f[i+1][j],f[i][j]); if((p=g[j][s1[i+1]])<l2+1)f[i+1][p]=min(f[i+1][p],f[i][j]+1); else ans=min(ans,f[i][j]+1); } if(ans==inf)ans=-1; printf("%d ",ans); return 0; }