可以说是出得十分好的一道dp题目了,要用到滚动数组,前缀和优化。
首先,我们考虑状态,f[k][i][j]表示第一个串到i位,第二个串到j位,分了k段的方案数。
首先,如果s1[i]==s2[j]的话,f[k][i][j]=sigema(f[k-1][l][j-1])(0<=l<i)的这都可以作为一个新段。特别的,如果s1[i-1]==s2[j-1]的话,及可以归到上一段,f[k][i][j]+=f[k-1][i-1][j-1]。
注意到这样状态是nmk的,第一个求和转移是n的,显然过不了啊,但是,我们发现,求和得很有规律,我们可以保存一下sigima(f[k][l][j])(0<=l<i),要用的时候O(1)转移就好了。这样,时间复杂度就成了O(nmk),可以过了。
但是,这样空间还是会承受不了,但我们发现f[k]都只和f[k-1]有关,就可以滚动数组解决了。
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int f[2][1005][205];
int tmp[2][1005][205];
int n,m,K,ans;
char A[1005],B[205];
int main()
{
freopen("in.txt","r",stdin);
scanf("%d%d%d",&n,&m,&K);
scanf("%s%s",A+1,B+1);
f[0][0][0]=1;
for(int i=0;i<=n;i++)tmp[0][i][0]=1;
for(int k=1;k<=K;k++)
{
memset(tmp[k&1],0,sizeof(tmp[k&1]));
memset(f[k&1],0,sizeof(f[k&1]));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(A[i]==B[j])
{
f[k&1][i][j]=tmp[(k+1)&1][i-1][j-1];
if(A[i-1]==B[j-1])f[k&1][i][j]=(f[k&1][i][j]+f[k&1][i-1][j-1])%mod;
}
tmp[k&1][i][j]=((tmp[k&1][i][j]+f[k&1][i][j])%mod+tmp[k&1][i-1][j])%mod;
}
}
for(int i=1;i<=n;i++)ans=(ans+f[K&1][i][m])%mod;
cout<<ans;
return 0;
}
总的来说,这道题出得是特别的好。