(mathcal{color{red}{Description}})
有两个仅包含小写英文字母的字符串 (A) 和 (B) 。
现在要从字符串 (A) 中取出 (k) 个互不重叠的非空子串,然后把这 (k) 个子串按照其在字符串 (A) 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 (B) 相等?
注意:子串取出的位置不同也认为是不同的方案。
对于所有 (10) 组数据:(1≤n≤1000,1≤m≤200,1≤k≤m) 。
(mathcal{color{red}{Solution}})
单纯的我一开始以为是一道状压……然后看到数据范围后是崩溃的……
仔细想了想,好像状压的确会枚举好多智障的冗余状态……那么就开始考虑正常向的(DP)……设(dp_{i,j,k})表示(A)中匹配到第(i)位,(B)中匹配到第(j)位,共用了(k)个子串的方案数。那么状态转移方程很显然就是(dp_{i,j,k} = (dp_{i-1,j-1,k} + dp_{i - 1, j - 1, k - 1}) imes [A_i = B_j])……
但想了想,发现是不对的……因为事实上我们没有考虑上一位没有选这种情况……于是我就不会惹……于是选择看题解(OTZ)。
然后发现自己被秀了一脸(qwq)
我们考虑用两个(DP)数组来记录状态,(f_{i,j,k})表示(A)中匹配到第(i)位,(B)中匹配到第(j)位,共用了(k)个子串,(A_i)这一位一定要用的的方案数。(dp_{i,j,k})表示(A)中匹配到第(i)位,(B)中匹配到第(j)位,共用了(k)个子串,(A_i)这一位可用可不用的方案数。
那么转移方程就是$$f_{i,j,k} = (dp_{i-1,j-1,k-1} + f_{i-1,j-1,k}) imes [A_i = B_j]$$和$$dp_{i,j,k} = dp_{i-1,j,k}+f_{i,j,k}$$第一个方程是基于是否和上一位连成一串得来的,第二个方程是基于是否选(A_i)的得来的。
滚啊滚啊~
#include <cstdio>
#include <cstring>
#include <iostream>
#define MAXN 1010
#define MAXM 210
#define mod 1000000007
using namespace std ;
char A[MAXN], B[MAXM] ; int i, j, k, d ;
int N, M, K, dp[2][MAXM][MAXM], f[2][MAXM][MAXM];
int main(){
cin >> N >> M >> K ; dp[0][0][0] = 1 ;
for(i = 1; i <= N; i ++) cin >> A[i] ;
for(i = 1; i <= M; i ++) cin >> B[i] ;
for(d = i = 1; i <= N; i ++, d ^= 1){
dp[d][0][0] = 1 ;
for(j = 1; j <= M; j ++)
for(k = 1; k <= K ;k ++){
if(A[i] == B[j]) f[d][j][k] = (dp[d ^ 1][j - 1][k - 1] + f[d ^ 1][j - 1][k]) % mod ;
else f[d][j][k] = 0 ;
dp[d][j][k] = (dp[d ^ 1][j][k] + f[d][j][k]) % mod ;
}
}
cout << dp[N & 1][M][K]% mod ;
}
一点想法:
突然发现不会做的(DP)和人生中好多事情都一样。总是缺乏迎难而上的勇气,总是在获得了不属于自己的东西之后沾沾自喜,总是在事情发生完了再追悔莫及。
唉,人啊…