【题解】Luogu P2679 [NOIP 2015 提高组] 子串
题意
统计:从串 \(A\) 中提取 \(k\) 个不相交子串,按前后顺序首尾相接可得到串 \(B\) 的方案数。子串的位置不同算作不同方案。答案对 \(10^9+7\) 取模。
思路
遇到这种“答案可能很大”的统计方案数的题多半是 DP。
设计状态
在这道题中,需要在 \(A\) 中匹配 \(B\),所以状态的第一维是 \(A\) 当前的位数 \(i\),第二维是 \(B\) 当前的位数 \(j\)。此外,还需要维护当前用了多少个子串,因此第三位是子串数 \(p\)。最后,要决定 \(A_i\) 是否参与匹配,所以第四维是 \(0\) 或 \(1\)。最后答案即为 \(f_{n,m,k,0}+f_{n,m,k,1}\)
转移状态
分情况考虑。
首先是 \(A_i\) 不参与匹配,也就是第四维为 \(0\),已匹配 \(B\) 的位数不变,已匹配子串数量也不变,那么该状态的方案数就是上一位选或不选的方案数之和。\(f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1}\)
然后是 \(A_i\) 参与匹配。如果 \(A_i \neq B_j\),显然 \(A_i\) 无法匹配,\(f_{i,j,p,1}=0\)。如果 \(A_i=B_j\),那么可以参与匹配,而这一位既可以加入上一位的子串(如果上一位参与匹配的话),也可以单独新开一个子串,所以 \(f_{i,j,p,1}=f_{i-1,j-1,p,1}+f_{i-1,j-1,p-1,0}+f_{i-1,j-1,p-1,1}\)。
状态初值
容易发现,对于每一位,\(B\) 的所有位都没有匹配过,都是一种合法方案,因此 \(f_{i,0,0,0}=1 (1\leq i\leq n)\)。
优化空间
四维 DP 的数组空间总大小为 \(1000\times 200\times 200\times 2=8\times 10^7\),对于 125MB 的空间来说是比较极限的,因此需要优化。
注意到,状态转移方程中,对于 \(f_i\) 只用到了 \(f_{i-1}\),因此可以把这一维滚动掉(取模 \(2\)),相应的也只需要对 \(f_0\) 和 \(f_1\) 赋值。
时间复杂度 \(O(nmk)\),\(4\times 10^7\) 的量级足够通过。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MOD=1e9+7;
int n,m,k;
char a[1010],b[210];
int f[2][210][210][2];
int main(){
scanf("%d%d%d",&n,&m,&k);
scanf("%s",a+1);
scanf("%s",b+1);
f[0][0][0][0]=f[1][0][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int p=1;p<=k;p++){
f[i%2][j][p][0]=(f[(i-1)%2][j][p][0]+f[(i-1)%2][j][p][1])%MOD;
if(a[i]==b[j]) f[i%2][j][p][1]=((f[(i-1)%2][j-1][p][1]+f[(i-1)%2][j-1][p-1][0])%MOD+f[(i-1)%2][j-1][p-1][1])%MOD;
else f[i%2][j][p][1]=0;
}
}
}
printf("%d\n",(f[n%2][m][k][1]+f[n%2][m][k][0])%MOD);
return 0;
}

浙公网安备 33010602011771号