Description
有两种字符串S,T。长度分别为n,m。现在需要在S里面有序地选出k个子串,且在T中出现的顺序与这k个子串的顺序相同。问这k个子串最大的长度和
Input
第一行三个数n,m,k
第二行长度为n的S串
第三行长度为m的T串
Output
一个数表示答案
Sample Input
9 12 4
bbaaababb
abbbabbaaaba
Sample Output
7
Data Constraint
30% n<=10,m<=20
100% n,m<=1000 k<=10
分析
一个显然的思路:设f[i][j][k]表示S串匹配到了第i位,j串匹配到了第j位,取了k个子串的最大值。转移:
f[i][j][k]=max{f[i’][j’][k-1]+len}
其中len是要枚举的,i’≤i-len,j’≤j-len。还要判断S[i-len+1,i]与T[j-len+1,j]是否匹配。
这个转移显然是
O(n3m2k)
的
优化
对于一个len,枚举的i’和j’可以看成一个矩形,然后可以设
s[i][j][k]=maxf[i′][j′][k]
,求这个的总复杂度是
O(nmk)
(s[i][j][k]=max{s[i][j-1][k],s[i-1][j][k],f[i][j][k]})
然后上面的方程就变成f[i][j][k]=max{s[i-len][j-len][k-1]+len}
进一步优化
我们发现转移的i-len与j-len的差等于i-j,所以对于相同的i-j和k,把所有s[i][j][k]放在一个单调队列里。时间复杂度降至 O(nmk)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1005;
int n,m,k,s[maxn][maxn][11],data[maxn*2][maxn][10],h[maxn*2][10],t[maxn*2][10],p[maxn][maxn];
char S[maxn],T[maxn];
int main()
{
freopen("string.in","r",stdin); freopen("string.out","w",stdout);
scanf("%d%d%d%s%s",&n,&m,&k,S+1,T+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) if (S[i]==T[j]) p[i][j]=p[i-1][j-1]+1;
memset(t,255,sizeof(t));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
int x=i-j+m,f;
for (int T=k-1;T;T--)
{
for (;h[x][T-1]<=t[x][T-1] && i-data[x][h[x][T-1]][T-1]>p[i][j];h[x][T-1]++);
f=(h[x][T-1]<=t[x][T-1])?s[data[x][h[x][T-1]][T-1]][data[x][h[x][T-1]][T-1]-i+j][T-1]+i-data[x][h[x][T-1]][T-1]:0;
s[i][j][T]=max(max(s[i-1][j][T],s[i][j-1][T]),f);
for (;h[x][T]<=t[x][T] && s[data[x][h[x][T]][T]][data[x][h[x][T]][T]-i+j][T]+i-data[x][h[x][T]][T]<=s[i][j][T];h[x][T]++);
data[x][++t[x][T]][T]=i;
}
f=p[i][j];
s[i][j][0]=max(max(s[i-1][j][0],s[i][j-1][0]),p[i][j]);
for (;h[x][0]<=t[x][0] && s[data[x][h[x][0]][0]][data[x][h[x][0]][0]-i+j][0]+i-data[x][h[x][0]][0]<=s[i][j][0];h[x][0]++);
data[x][++t[x][0]][0]=i;
}
int ans=0;
for (int i=0;i<k;i++) ans=max(ans,s[n][m][i]);
printf("%d\n",ans);
fclose(stdin); fclose(stdout);
return 0;
}