洛谷P2679 子串
? 解题记录 ? ? 动态规划 ? ? 洛谷 ?    2017-08-21 14:32:25    687    0    0

题目背景

题目描述

有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。

输入输出格式

输入格式:

 

输入文件名为 substring.in。

第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问

题描述中所提到的 k,每两个整数之间用一个空格隔开。 第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。

 

输出格式:

 

输出文件名为 substring.out。 输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求[b]输出答案对 1,000,000,007 取模的结果。[/b]

 

输入输出样例

输入样例#1:
6 3 1 
aabaab 
aab
输出样例#1:
2
输入样例#2:
6 3 2 
aabaab 
aab
输出样例#2:
7
输入样例#3:
6 3 3 
aabaab 
aab
输出样例#3:
7

说明

对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;

对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2; 对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m; 对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m; 对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m; 对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。

这道DP题十分值得研究。想了很久还是向题解低头了……

对于一道DP,我们应该想清楚几件事情:状态数组怎么定义、每一个状态从哪里来、边界条件是什么。首先看这道题,是对字符串A、B进行操作。于是我们的前两个维度i,j可以表示A串到i处、B串到j处。又因为限制条件有一条:截取K个子串。我们可以先再加一个维度k,表示现在状态在A串中截取了K个子串。但是有一个问题:那就是根据当前字符匹不匹配可以创生出两种情况:在A中选择当前字符、不选择当前字符。 于是我们可以加上第四维:0/1, 表示不选择、选择当前字符。

接下来我们应该考虑状态转移方程了。假设dp[i][j][k][o]为当前状态。显然我们要同时讨论两种状态:o 为1 还是 0。

o=0 : 也就是当前字符不选择,那么我们取的子串数不会变,B串位置不会变于是当前dp[i][j][k][0] = dp[i-1][j][k][1] + dp[i-1][j][k][0]。

o=1 : 也就是当前字符选择。当然,条件是两字符串的i,j处相等。这个时候我们考虑它能怎样被转移,就有了两种情况:1、上一个状态选到了上一个字符,接着上一个状态最后的子串选,那么子串数不变。即dp[i-1][j-1][k][1]。2、选择新的字符,无论上一个状态选没选到上一个字符,都可以断开上次选择的子串新选一个子串,也就是dp[i-1][j-1][k-1][0] + dp[i-1][j-1][k-1][1]。

于是,o=1时总的递推式为dp[i][j][k][1] = dp[i-1][j-1][k][1] + dp[i-1][j-1][k-1][0] + dp[i-1][j-1][k-1][1]。

最后,我们来想一想边界条件,我们枚举每一个A串选择的位置(也就是i)的时候,如果B串一个都没选(j = 0),在A串中已选子串数目为0(k = 0),并且最后一个没有选择(o = 0)一定成立,于是对于每一个i,dp[i][0][0][0] = 1。

下面放出一段小小的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[2][205][205][2];
int n, m, k;
char A[1005], B[205];
const int modp = 1000000007;

int main() {
    scanf("%d%d%d", &n, &m, &k);
    scanf("%s", A);
    scanf("%s", B);
    /**/
    dp[0][0][0][0] = 1;
    for(register int i = 1; i <= n; ++i) {
        dp[i & 1][0][0][0] = 1;
        for(register int j = 1; j <= m; ++j) 
            for(register int t = 0; t <= k; ++t) {
                dp[i & 1][j][t][1] = 0;
                dp[i & 1][j][t][0] = (dp[(i & 1) ^ 1][j][t][1] % modp + dp[(i & 1) ^ 1][j][t][0] % modp) % modp;
                if(A[i - 1] == B[j - 1])
                    dp[i & 1][j][t][1] = ((dp[(i & 1) ^ 1][j - 1][t - 1][0] % modp + dp[(i & 1) ^ 1][j - 1][t][1] % modp) % modp + dp[(i & 1) ^ 1][j - 1][t - 1][1] % modp) % modp;
            }
        }
    printf("%d\n", (dp[n & 1][m][k][0] % modp + dp[n & 1][m][k][1] % modp) % modp);
    return 0;
}

 

上一篇: 洛谷P3811【模板】乘法逆元

下一篇: 洛谷 P2680 运输计划

687 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航