个人觉得dp的边界条件应该是由状态转移方程所确定的 一般先确定状态转移方程 在通过合理性去判断边界条件
这个题题意很好理解 给你两个串 s和t 用s去构建一个新串 每次按顺序取出s中的一个字母 可以加在新串的开头 或者结尾 问有多少种方法让新串的前缀是t
如果要让新串的前缀是t串的话 那么s显然要通过某种方式和t串进行一个匹配 通过题意知道s串在取字母的时候可以放新串的前面或者后面 那我们就根据这个模式匹配
对于s串 我们枚举每个字母 对于t串我们枚举一个区间 显然要匹配成功的话 得满足
这其实就相当于把两个串进行区间dp了 我们用 表示t字符串在 区间l到r和串s匹配的方法数
那我们讨论一下:
- 当s[i]==t[l]时 t字符串左端匹配 dp[l][r]+=dp[l+1][r] 相当于把s串的第i个字母前插
- 当s[i]==t[r]时 t字符串右端匹配 dp[l][r]+=dp[l][r-1] 相当于把s串的第i个字母后插
需要注意的是t串一般长度小于s串 因为只要前缀为t 那么长度大于t的字母其实是随意的 我们可以看第一个样例
abab ba**
后面两个*和谁匹配都无所谓
所以当 l>m时 l和谁匹配无所谓 直接转移即可 r也是同理(m是t串长度)
边界条件:当l==r时(t串的枚举长度为1) 如果此时s[i]和t[l](t[r]) 匹配上了(或者l>m) 那么s[i]前插还是后插都无所谓
由此我们可以确定 dp[i][i-1]=1 (这样转移过后dp[l][r]=2 符号题意)
最终答案: 因为t串长为m 后面都是*了 根据dp方程的定义从m往后都满足条件
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 998244353;
const int N = 3e3+100;
char s[N],t[N];
ll dp[N][N];
int main(){
scanf("%s%s",s+1,t+1);
int n = strlen(s+1);
int m = strlen(t+1);
for(int i=1; i <= n+10;i++)
dp[i][i-1]=1;
for(int i=1, len=1; i<=n ;i++, len++)
for(int l=1, r=l+len-1; r<=n ;l++, r++){
if(l>m||s[i]==t[l]) dp[l][r]=(dp[l][r]+dp[l+1][r])%M;
if(r>m||s[i]==t[r]) dp[l][r]=(dp[l][r]+dp[l][r-1])%M;
}
ll ans = 0;
for(int i=m; i<=n ;i++)
ans = (ans + dp[1][i]) % M;
/*for(int j = 1; j <= n; j++){
if(j==n) printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);
else printf("dp[%d][%d]=%lld ",i,j,dp[i][j]);
}*/
printf("%lld\n",ans);
return 0;
}