令
fi,j
表示
s
串前
对于
fi,j
,可以更新:
- fi+1,j=max(fi+1,j,fi,j)
- fi+lcp,j+1=max(fi+lcp,j+1,fi,j+lcp)
其中
lcp
表示
s[i+1..n]
与
t[fi,j+1..m]
的最长公共前缀,可以用二分+
Hash
求出。
时间复杂度
O(nxlogn)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define P1 127
#define M1 1000000007
#define P2 31
#define M2 998244353
//long long s1[N],s2[N],c[N];
long long s1[2][N],s2[2][N],c[2][N];
int i,j,k,n,m,p,f[N][40];
char s[2][N];
inline int Max(int x,int y){
return x<y?y:x;
}
inline int Min(int x,int y){
return x<y?x:y;
}
inline bool Check(int l1,int r1,int l2,int r2){
return ((s1[0][r1]-s1[0][l1-1]*c[0][r1-l1+1])%M1+M1)%M1==((s2[0][r2]-s2[0][l2-1]*c[0][r2-l2+1])%M1+M1)%M1&&
((s1[1][r1]-s1[1][l1-1]*c[1][r1-l1+1])%M2+M2)%M2==((s2[1][r2]-s2[1][l2-1]*c[1][r2-l2+1])%M2+M2)%M2;
}
inline int Lcp(int x,int y){
int l=x,r=Min(n,m+x-y),Mid;
while(l<=r){
Mid=l+r>>1;
if(Check(x,Mid,y,Mid-x+y))l=Mid+1;else r=Mid-1;
}
return r;
}
int main(){
scanf("%d%s%d%s%d",&n,s[0]+1,&m,s[1]+1,&k);
for(i=c[0][0]=1;i<=n;c[0][i]=c[0][i-1]*P1%M1,i++)s1[0][i]=(s1[0][i-1]*P1+s[0][i]-'a'+1)%M1;
for(i=1;i<=m;i++)s2[0][i]=(s2[0][i-1]*P1+s[1][i]-'a'+1)%M1;
for(i=c[1][0]=1;i<=n;c[1][i]=c[1][i-1]*P2%M2,i++)s1[1][i]=(s1[1][i-1]*P2+s[0][i]-'a'+1)%M2;
for(i=1;i<=m;i++)s2[1][i]=(s2[1][i-1]*P2+s[1][i]-'a'+1)%M2;
for(i=0;i<=n;i++)
for(j=0;j<=k;j++){
f[i+1][j]=Max(f[i][j],f[i+1][j]);
if(j<k){
p=Lcp(i+1,f[i][j]+1);
if(p>i)f[p][j+1]=Max(f[p][j+1],p-i+f[i][j]);
}
}
for(j=1;j<=k;j++)
if(f[n][j]==m)return printf("YES\n"),0;
printf("NO\n");
return 0;
}