UOJ 149 子串

 统计

有两个仅包含小写英文字母的字符串  A A 和  B B

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

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

输入格式

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

第二行包含一个长度为  n n 的字符串,表示字符串  A A

第三行包含一个长度为  m m 的字符串,表示字符串  B B

输出格式

输出共一行,包含一个整数,表示所求方案数。

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

样例一

input
6 3 1
aabaab
aab

output
2

样例二

input
6 3 2
aabaab
aab

output
7

样例三

input
6 3 3
aabaab
aab

output
7

explanation

所有合法方案如下:(加下划线的部分表示取出的子串)

样例一:aab aab / aab aab

样例二:a ab aab / a aba ab / a a ba ab / aab a ab / aa b aab / aa baa b / aab aa b

样例三:a a b aab / a a baa b / a ab a a b / a aba a b / a a b a a b / a a ba a b / aab a a b

限制与约定

测试点编号 n n的规模 m m的规模 k k的规模
1 n500 n≤500 m50 m≤50 k=1 k=1
2 k=2 k=2
3
4 k=m k=m
5
6 km k≤m
7
8 n1000 n≤1000 m100 m≤100
9
10 m200 m≤200

时间限制: 1s 1s

空间限制: 128MB


看过去就知道是DP啊!!本题超多细节高能。

设f[i][j][k][0/1]表示A串的前i位取出k个子串匹配了B串的前j位,选或不选第i位。

分两种情况1.若A串第i位与B串第j位匹配,那么f[i][j][k][1]=f[i-1][j-1][k][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1].

解释一下:若匹配,则选择这一位的情况有如下几种累加方式:(首先B串的匹配个数都要-1)前一位也选并与当前位构成同一个子串(子串个数不变)、前一位选并且不断点为这一位、前一位不选(子串个数都要-1)。

f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1].  解释一下:若不选择这一位,当前方式可以累加的有:(B串的匹配个数不变)前一位选或者前一位不选,子串个数不变(因为这一位不选)

2.若A串第i位与B串第j位不匹配,则f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1].解释:若不匹配,该位肯定不能选。所以不选的方案数累加上前一位选或者不选的个数。


#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>

using namespace std;

const int MAXN=1010;
const int MAXM=220;
const int Mod=1e9+7;
long long f[2][MAXM][MAXM][2];

int main()
{
	char a[MAXN],b[MAXM];
	int n,m,k,cnt=0,last=-1;
	scanf("%d%d%d",&n,&m,&k);
	scanf("%s",a+1);
	scanf("%s",b+1);
	f[0][0][0][0]=1;
	for (int i=1;i<=n;i++)
	{
		cnt++;
		cnt%=2;
		last++;
		last%=2;
		for (int j=0;j<=m;j++)
		{
			for (int p=0;p<=k;p++)
			{
				if (a[i]==b[j])
				{
					if (j>0)
					{
						if (p>0)
						{
							f[cnt][j][p][1]=f[last][j-1][p][1]+f[last][j-1][p-1][0]+f[last][j-1][p-1][1];
							f[cnt][j][p][1]%=Mod;
						}
					}
					f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];
					f[cnt][j][p][0]%=Mod;
				}
				else
				{
					f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];
					f[cnt][j][p][0]%=Mod;
				}
			}
		}
		for (int j=0;j<=m;j++)
		{
			for (int p=0;p<=k;p++)
			{
				f[last][j][p][0]=0;
				f[last][j][p][1]=0;
			}
		}
	}
	cout<<(f[n%2][m][k][1]+f[n%2][m][k][0])%Mod;
    return 0;
}
需要开滚动数组不然会MLE,滚动数组每用完一次后都要把当前清零。

再次吐槽超多细节(NOIP还是坑人啊)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值