P2679 子串

本文介绍了一种利用动态规划解决特定字符串匹配问题的方法。给定两个字符串AA和BB及一个整数KK,文章探讨了如何找出AA中KK个非空且不重叠的子串,按顺序拼接后等于BB的有效方案数量,并给出了具体的实现代码。
摘要由CSDN通过智能技术生成

题目描述

有两个仅包含小写英文字母的字符串 AA 和 BB。

现在要从字符串 AA 中取出 kk 个互不重叠的非空子串,然后把这 kk 个子串按照其在字符串 AA 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 BB 相等?

注意:子串取出的位置不同也认为是不同的方案。

输入格式

第一行是三个正整数 n,m,kn,m,k,分别表示字符串 AA 的长度,字符串 BB 的长度,以及问题描述中所提到的 kk,每两个整数之间用一个空格隔开。

第二行包含一个长度为 nn 的字符串,表示字符串 AA。

第三行包含一个长度为 mm 的字符串,表示字符串 BB。

输出格式

一个整数,表示所求方案数。

由于答案可能很大,所以这里要求输出答案对 10000000071000000007 取模的结果。

输入输出样例

输入 #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=11≤n≤500,1≤m≤50,k=1;
对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=21≤n≤500,1≤m≤50,k=2;
对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m1≤n≤500,1≤m≤50,k=m;
对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m1≤n≤500,1≤m≤50,1≤k≤m;
对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m1≤n≤1000,1≤m≤100,1≤k≤m;
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m1≤n≤1000,1≤m≤200,1≤k≤m。

 

思路:

刚开始想到的状态是

dp[i][j][k]:a字符串的前i个匹配b字符串的前j个用k个子串。

但是,这样还不够,因为第i个字符的使用情况会影响第i-1个字符的使用情况。

为什么呢?见下面的情况分析。

所以我们还需要一维来表示使用与否。  使用过用1,没用过用0,即 dp[i][j][k][0/1].

当a[i]==b[j],那么第i个字符可以使用也可以不使用。

使用: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];

dp[i-1][j-1][k][1]:使用第i个字符并且跟前面的第i-1个字符连接成为第k个子串,所以第i-1个字符必须使用。(即跟第i个字符有关)

dp[i-1][j-1][k-1][0]、dp[i-1][j-1][k-1][1] :当第i个字符单独成新的子串,那么第i-1个字符可用可不用(即跟第i个字符无关)

不使用:

dp[i][j][k][0]=dp[i-1][j][k][1]+dp[i-1][j][k][0]  (即跟第i个字符无关)

 

当a[i]!=b[j]时,

不使用:

dp[i][j][k][0]=dp[i-1][j][k][1]+dp[i-1][j][k][0]  (即跟第i个字符无关)

使用: dp[i][j][k][1]=0;

 综上所述,有

if(a[i]==b[j]){

     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];

}

else  dp[i][j][k][1]=0;

dp[i][j][k][0]=dp[i-1][j][k][1]+dp[i-1][j][k][0] ;

 

关键代码就好啦~

边界值 dp[0][0][0][0]=1 dp[1][0][0][0][0]=1

假如开四维数组,根据数据范围有1000*200*200*2,很有可能卡空间。

通过转移方程发现,每次i行都是通过i-1行更新得到的,所以我们只要开两行的空间,来回使用,即滚动数组就好了。

#include<bits/stdc++.h>
using namespace std;
char a[1005],b[205];
int dp[2][205][205][2];
const int mod=1000000007;
int main(){
	int n,m,k;
	scanf("%d%d%d%s%s",&n,&m,&k,a+1,b+1);
	dp[0][0][0][0]=1;
	dp[1][0][0][0]=1;
	for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
              for(int g=1; g<=k; g++){
                 if(a[i]==b[j]){
                    dp[i%2][j][g][1]=((dp[(i-1)%2][j-1][g-1][0]+dp[(i-1)%2][j-1][g-1][1])%mod+dp[(i-1)%2][j-1][g][1])%mod;
                 }
                 else{
                    dp[i%2][j][g][1]=0;
                 }
                  dp[i%2][j][g][0]=(dp[(i-1)%2][j][g][0]+dp[(i-1)%2][j][g][1])%mod;//不使用第i个
                }
           }
	}
    cout<<(dp[n%2][m][k][0]+dp[n%2][m][k][1])%mod<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值