洛谷P2679 [NOIP2015 提高组] 子串 题解
题意:
有两个仅包含小写英文字母的字符串 $A$ 和 $B$。
现在要从字符串 $A$ 中取出 $d$ 个互不重叠的非空子串,然后把这 $d$ 个子串按照其在字符串 $A$ 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 $B$ 相等?
注意:子串取出的位置不同也认为是不同的方案。
输出答案对 $10^9+7$ 取模的结果。
对于所有 $10$ 组数据: $1\le n \le 1000,~1 \le m \le 200,~1 \le d \le m$。
显然的字符串类型dp
一个原始的思路是:
设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串的方案数。
然后发现我们并不知道 $A_i$ 是否与 $B_j$ 匹配,无法转移
于是状态就变成了
设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串,且 $A_i$ 为最后一个匹配字符的方案数。
这样就可以转移了
显然 $i$ 这一维可以用滚动数组滚掉
那么 $\sum_{t=1}^{p+1} f_{i-t,j-t,k-1}$ 怎么处理呢
其实我们已经在之前的计算中把 $f_{i-t,j-t,k-1}$ 计算出来了
考虑动态维护一个前缀和,在遇到 $A_i\ne B_j$ 的时候中断,具体见代码
时间复杂度 $O(nmk)$
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(205)
const int p=1e9+7;
char a[1005],b[N];
int n,m,d,f[N][N],sum[N][N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
f[0][0]=1;
cin >> n >> m >> d >> a >> b;
for(int i=1; i<=n; i++)
for(int j=m; j>=1; j--)
for(int k=d; k>=1; k--)
{
if(a[i-1]==b[j-1])
sum[j][k]=sum[j-1][k]+f[j-1][k-1];
else sum[j][k]=0;
f[j][k]=(f[j][k]+sum[j][k]%p)%p;
}
cout << f[m][d] << '\n';
return 0;
}