[Luogu P2679] [UOJ 149] [NOIP 2015 tg] 子串

本文解析了一道洛谷平台上的字符串匹配问题,通过动态规划方法解决如何从字符串AAA中选取kkk个非重叠子串,使其拼接后与字符串BBB相等。详细介绍了状态转移方程和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
UOJ传送门

题目描述

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

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

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

输入输出样例

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

说明

img

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

解题分析

d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j]表示已经分了 k k k段, A A A串匹配到 i i i B B B串恰好匹配到 j j j的方案数, 当 A [ i ] = B [ j ] A[i]=B[j] A[i]=B[j]的时候有
d p [ k ] [ i ] [ j ] = ∑ i = 1 j − 1 d p [ k − 1 ] [ i ] [ j − 1 ] dp[k][i][j]=\sum_{i=1}^{j-1}dp[k-1][i][j-1] dp[k][i][j]=i=1j1dp[k1][i][j1]
如果 A [ i − 1 ] = B [ j − 1 ] A[i-1]=B[j-1] A[i1]=B[j1]的话, d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j]还要加上 d p [ k ] [ i − 1 ] [ j − 1 ] dp[k][i-1][j-1] dp[k][i1][j1], 等于合并成一个区间的方案。

然后我们发现上面的那个式子就是个前缀和, 直接处理出来, 而且整个方程只和 k , k − 1 k,k-1 k,k1有关, 所以滚动一维。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#define R register
#define IN inline
#define W while
#define File freopen("t2.in", "r", stdin),  freopen("t2.out", "w", stdout)
#define gc getchar()
#define ll long long
#define MOD 1000000007
#define MX 1050
int dp[2][MX][205];
int sum[MX][205];
char buf1[MX], buf2[MX];
int n, m, tim;
int main(void)
{
	R int i, j, k, cur = 0, pre;
	scanf("%d%d%d", &n, &m, &tim);
	scanf("%s%s", buf1 + 1, buf2 + 1);
	buf1[0] = '#', buf2[0] = '$';
	for (j = 0; j <= n; ++j)
	sum[j][0] = 1;
	for (i = 1; i <= tim; ++i)
	{
		pre = cur, cur ^= 1;
		std::memset(dp[cur], 0, sizeof(dp[cur]));
		for (j = 1; j <= n; ++j)
		for (k = 1; k <= m; ++k)
		{
			if(buf1[j] == buf2[k])
			{
				dp[cur][j][k] = sum[j - 1][k - 1];
				if(buf1[j - 1] == buf2[k - 1]) (dp[cur][j][k] += dp[cur][j - 1][k - 1]) %= MOD;
			}
		}
		std::memset(sum, 0, sizeof(sum));
		for (j = 1; j <= n; ++j)
		for (k = 1; k <= m; ++k)
		sum[j][k] = (sum[j - 1][k] + dp[cur][j][k]) % MOD;
	}
	for (i = 1; i <= n; ++i) dp[cur][i][m] = (dp[cur][i - 1][m] + dp[cur][i][m]) % MOD;
	printf("%d", dp[cur][n][m]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值