题目描述
难度分:2200
输入长度不超过3000的字符串S,只包含小写字母。设S的长度为n。
输入长度不超过n的字符串T,只包含小写字母。
从一个空字符串A开始,执行如下操作不超过n次:
- 删除S的第一个字母,然后加到A的开头或者末尾。
问:要使T是A的前缀,有多少种不同的操作方式?模998244353。
注:即使两个不同的操作方式得到了相同的字符串A,也算不同的操作方式。
输入样例1
abab
ba
输出样例1
12
输入样例2
defineintlonglong
signedmain
输出样例2
0
输入样例3
rotator
rotator
输出样例3
4
输入样例4
cacdcdbbbb
bdcaccdbbb
输出样例4
24
算法
动态规划
这个题主要的难度就在于状态的设计,设原串s的长度为n,t的长度为m。
状态定义
f[l][r]表示用s前r−l+1个字符拼出t[l…r]的方案数,由于新串A的长度len最多就是n(原串s的长度),所以答案应该就是Σnlen=mf[1][len]
状态转移
base case:用s的第一个字符得到t[i]的方案数,当s[1]=t[i]且i>m时,f[i][i]=2(可以头插也可以尾插,所以有两种方案)。
一般情况:当要用长度为len的s前缀构造t[l…r]时,需要满足l∈[1,n+1−len]。假设头插,就需要满足s[len]=t[l]或l≥m,此时就是在t[l+1…r]上转移过来的,状态转移方程为f[l][r]=f[l+1][r];假设尾插,就需要满足s[len]=t[r]或r≥m,此时是在t[l…r−1]上转移过来的,状态转移方程为f[l][r]=f[l][r−1]。并且两种情况可能都存在,那状态转移方程就是f[l][r]=f[l+1][r]+f[l][r−1]。
复杂度分析
时间复杂度
枚举长度时间复杂度O(n),枚举左端点l时间复杂度O(n)。单次状态转移是O(1)的,所以算法整体的时间复杂度为O(n2)。
空间复杂度
空间瓶颈在于DP
数组f,它是n2级别的,因此算法额外空间复杂度为O(n2)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3010, MOD = 998244353;
int n, m, f[N][N];
char s[N], t[N];
int main() {
scanf("%s", s + 1);
scanf("%s", t + 1);
n = strlen(s + 1);
m = strlen(t + 1);
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i++) {
f[i][i] = ((m < i || s[1] == t[i])? 1: 0)<<1;
}
for(int len = 2; len <= n; len++) {
for(int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if(m < l || s[len] == t[l]) {
f[l][r] = (f[l][r] + f[l + 1][r]) % MOD; // 放前面
}
if(m < r || s[len] == t[r]) {
f[l][r] = (f[l][r] + f[l][r - 1]) % MOD; // 放后面
}
}
}
int ans = 0;
for(int len = m; len <= n; len++) {
ans = (ans + f[1][len]) % MOD;
}
printf("%d\n", ans);
return 0;
}