题解:洛谷 P2679 [NOIP 2015 提高组] 子串
【题目来源】
洛谷:P2679 [NOIP 2015 提高组] 子串 - 洛谷
【题目描述】
有两个仅包含小写英文字母的字符串 \(A\) 和 \(B\)。
现在要从字符串 \(A\) 中取出 \(k\) 个互不重叠的非空子串,然后把这 \(k\) 个子串按照其在字符串 \(A\) 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 \(B\) 相等?
注意:子串取出的位置不同也认为是不同的方案。
【输入】
第一行是三个正整数 \(n,m,k\),分别表示字符串 \(A\) 的长度,字符串 \(B\) 的长度,以及问题描述中所提到的 \(k\),每两个整数之间用一个空格隔开。
第二行包含一个长度为 \(n\) 的字符串,表示字符串 \(A\)。
第三行包含一个长度为 \(m\) 的字符串,表示字符串 \(B\)。
【输出】
一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 \(1000000007\) 取模的结果。
【输入样例】
6 3 1
aabaab
aab
【输出样例】
2
【解题思路】

【算法标签】
《洛谷 P2679 子串》#字符串# #动态规划,dp# #枚举# #NOIP提高组# #O2优化# #2015#
【代码详解】
// 70分版本
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
const int MOD = 1000000007; // 定义模数
// 四维DP数组:
// dp[i][j][k][0] - 前i个s1和前j个s2使用k次匹配且不以s1[i]结尾的方案数
// dp[i][j][k][1] - 前i个s1和前j个s2使用k次匹配且以s1[i]结尾的方案数
int dp[505][55][55][2];
int n, m, x; // n: s1长度, m: s2长度, x: 最大匹配次数
string s1, s2; // 输入的两个字符串
signed main()
{
// 输入参数和字符串
cin >> n >> m >> x >> s1 >> s2;
// 在字符串前添加空格,使索引从1开始
s1 = " " + s1;
s2 = " " + s2;
// 初始化DP数组:空字符串有1种匹配方式
for (int i = 0; i <= n; i++)
{
dp[i][0][0][0] = 1;
}
// 动态规划填表
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int k = 1; k <= x; k++)
{
// 不选择s1[i]作为匹配结尾的情况
dp[i][j][k][0] = (dp[i-1][j][k][0] + dp[i-1][j][k][1]) % MOD;
// 选择s1[i]作为匹配结尾的情况
if (s1[i] == s2[j])
{
dp[i][j][k][1] = (dp[i-1][j-1][k][1] +
dp[i-1][j-1][k-1][1] +
dp[i-1][j-1][k-1][0]) % MOD;
}
else
{
dp[i][j][k][1] = 0; // 字符不匹配,方案数为0
}
}
}
}
// 输出结果:总方案数取模
cout << (dp[n][m][x][0] + dp[n][m][x][1]) % MOD;
return 0;
}
// 100分版本,使用滚动数组
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义int为long long类型
const int MOD = 1000000007; // 定义模数
// 滚动数组优化后的四维DP数组:
// dp[0/1][j][k][0] - 前i个s1和前j个s2使用k次匹配且不以s1[i]结尾的方案数
// dp[0/1][j][k][1] - 前i个s1和前j个s2使用k次匹配且以s1[i]结尾的方案数
int dp[2][205][205][2];
int n, m, x; // n: s1长度, m: s2长度, x: 最大匹配次数
string s1, s2; // 输入的两个字符串
signed main()
{
// 输入参数和字符串
cin >> n >> m >> x >> s1 >> s2;
// 在字符串前添加空格,使索引从1开始
s1 = " " + s1;
s2 = " " + s2;
// 初始化DP数组:空字符串有1种匹配方式
for (int i = 0; i <= 2; i++)
{
dp[i][0][0][0] = 1;
}
// 动态规划填表(使用滚动数组优化空间)
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int k = 1; k <= x; k++)
{
// 不选择s1[i]作为匹配结尾的情况
dp[1][j][k][0] = (dp[0][j][k][0] + dp[0][j][k][1]) % MOD;
// 选择s1[i]作为匹配结尾的情况
if (s1[i] == s2[j])
{
dp[1][j][k][1] = (dp[0][j-1][k][1] +
dp[0][j-1][k-1][1] +
dp[0][j-1][k-1][0]) % MOD;
}
else
{
dp[1][j][k][1] = 0; // 字符不匹配,方案数为0
}
}
}
// 滚动数组:将当前状态复制到前一状态
memcpy(dp[0], dp[1], sizeof(dp[0]));
}
// 输出结果:总方案数取模
cout << (dp[1][m][x][0] + dp[1][m][x][1]) % MOD;
return 0;
}
【运行结果】
6 3 1
aabaab
aab
2
浙公网安备 33010602011771号