Description
在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。
一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
Input
有两行,每行一个小写字母组成的字符串,分别代表A和B。
Output
输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.
对B建出后缀自动机和序列自动机
(1)(2)问直接枚举A子串的左端点,找在自动机上第一个不能匹配到的右端点
(3)(4)问用f[i][j]表示考虑了A串的前i个字符,在自动机上匹配到节点j的最短子序列长度,答案为f[len(A)][null]
时间复杂度O(len(A)*len(B)+len(B)*26)
#include<cstdio> #include<cstring> const int inf=0x3f3f3f3f; char s1[2007],s2[2007]; int nx[4111][26],l[4111],fa[4111],pv=1,ptr=1,f[4007],g[4007]; int snx[2007][26],sp=1; void mins(int&a,int b){if(a>b)a=b;} int main(){ scanf("%s%s",s1+1,s2+1); int l1=strlen(s1+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){ int x=s2[i]; for(int j=sp++;j&&!snx[j][x];--j)snx[j][x]=sp; int p=pv,np=++ptr;pv=np; l[np]=l[p]+1; while(p&&!nx[p][x])nx[p][x]=np,p=fa[p]; if(!p)fa[np]=1; else{ int q=nx[p][x]; if(l[q]==l[p]+1)fa[np]=q; else{ int nq=++ptr; memcpy(nx[nq],nx[q],26); l[nq]=l[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; while(p&&nx[p][x]==q)nx[p][x]=nq,p=fa[p]; } } } int ans=inf; for(int i=1;i<=l1;++i){ for(int j=i,w=1;j<=l1;++j){ w=nx[w][s1[j]]; if(!w){ mins(ans,j-i+1); break; } } } printf("%d\n",ans==inf?-1:ans); ans=inf; for(int i=1;i<=l1;++i){ for(int j=i,w=1;j<=l1;++j){ w=snx[w][s1[j]]; if(!w){ mins(ans,j-i+1); break; } } } printf("%d\n",ans==inf?-1:ans); memset(f,0x3f,sizeof(int)*(ptr+1)); memset(g,0x3f,sizeof(int)*(ptr+1)); f[1]=g[1]=0; for(int i=1;i<=l1;++i){ int x=s1[i]; for(int j=1;j<=ptr;++j){ mins(g[nx[j][x]],f[j]+1); } memcpy(f,g,sizeof(int)*(ptr+1)); } printf("%d\n",f[0]==inf?-1:f[0]); memset(f,0x3f,sizeof(int)*(sp+1)); memset(g,0x3f,sizeof(int)*(sp+1)); f[1]=g[1]=0; for(int i=1;i<=l1;++i){ int x=s1[i]; for(int j=1;j<=sp;++j){ mins(g[snx[j][x]],f[j]+1); } memcpy(f,g,sizeof(int)*(sp+1)); } printf("%d\n",f[0]==inf?-1:f[0]); return 0; }