专栏文章
题解:P2679 [NOIP 2015 提高组] 子串
P2679题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mipv00ln
- 此快照首次捕获于
- 2025/12/03 18:23 3 个月前
- 此快照最后确认于
- 2025/12/03 18:23 3 个月前
P2679 [NOIP 2015 提高组] 子串
题目
给出两个字符串 和 ,在 中取出 个互不重叠的子串,使得这些子串按顺序拼接在一起时等于 ,求方案数。
状态
一看是神秘的方案题,很容易想到 dp。为了方便,可以用数组 和 存下 和 的 ASCII 码方便转移和优化复杂度,这样做是正确的原因是只关注两个字符是否相等而不是其他性质。
我们可以令 表示此时枚举数组 前 都已算完,到第 项,枚举数组 前 项都已算完,到第 项,还可以取出 个子串时的状态。
这是常规的设计状态的思路,但我们很快发现,这样设计时不好辨别选取该项时是不是和上一个选的是同一个子串,于是我们便要增加维度。
令 ,其中 ,, 意思同上, 表示不取数组 中的第 项, 表示取。
容易发现,该状态可以在转移的时候进行判断,于是就可以开始状态转移。
转移
转移的时候进行分类讨论。
-
当 时,。
-
当 时, 不能匹配 ,只能从前面的状态进行转移,即:
- 当 时,可以用 匹配 ,此时要么 的选了可以成为一个子串,要么新开一个子串,即:
Code
CPP#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3+1 , M = 201 , inf = 1e9+7;
int n , m , k , a[N] , b[M];
LL f[N][M][M][2];
string in;
int main()
{
ios::sync_with_stdio (false);
cin >> n >> m >> k;
cin >> in;
for(int i = 1 ; i <= n ; i ++)
{
a[i] = in.at(i-1) - 'a' + 1;
}
cin >> in;
for(int i = 1 ; i <= m ; i ++)
{
b[i] = in.at(i-1) - 'a' + 1;
}
f[0][0][0][0] = 1;
for(int i = 1 ; i <= n ; i ++)
{
f[i][0][0][0] = 1;
for(int j = 1 ; j <= m && j <= i ; j ++)
{
for(int t = 1 ; t <= k ; t ++)
{
f[i][j][t][0] = (f[i-1][j][t][0] + f[i-1][j][t][1]) % inf;
if(a[i] == b[j])
{
f[i][j][t][1] = (f[i-1][j-1][t][1] + f[i-1][j-1][t-1][0] + f[i-1][j-1][t-1][1]) % inf;
}
}
}
}
cout << (f[n][m][k][0] + f[n][m][k][1]) % inf << "\n";
return 0;
}
然而,只有 分。
这么大的空间 谁不迷糊。
可以发现每个状态的 只依赖于 ,于是可以滚动数组优化。
用取模运算或交替覆盖方式访问滚动数组。这里取模 可以近似看作位运算 &。
AC Code
CPP#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3+1 , M = 201 , inf = 1e9+7;
int n , m , k , a[N] , b[M];
LL f[2][M][M][2];
string in;
int main()
{
ios::sync_with_stdio (false);
cin >> n >> m >> k;
cin >> in;
for(int i = 1 ; i <= n ; i ++)
{
a[i] = in.at(i-1) - 'a' + 1;
}
cin >> in;
for(int i = 1 ; i <= m ; i ++)
{
b[i] = in.at(i-1) - 'a' + 1;
}
f[0][0][0][0] = f[1][0][0][0] = 1;
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= m && j <= i ; j ++)
{
for(int t = 1 ; t <= k ; t ++)
{
f[i&1][j][t][0] = (f[(i-1)&1][j][t][0] + f[(i-1)&1][j][t][1]) % inf;
if(a[i] == b[j])
{
f[i&1][j][t][1] = (f[(i-1)&1][j-1][t][1] + f[(i-1)&1][j-1][t-1][0] + f[(i-1)&1][j-1][t-1][1]) % inf;
}
else f[i&1][j][t][1] = 0;
// 这里注意,因为把数组复用了,转移不了的要清零。
}
}
}
cout << (f[n&1][m][k][0] + f[n&1][m][k][1]) % inf << "\n";
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...