E. Kaavi and Magic Spell 区间dp

个人觉得dp的边界条件应该是由状态转移方程所确定的 一般先确定状态转移方程 在通过合理性去判断边界条件

这个题题意很好理解 给你两个串 s和t 用s去构建一个新串  每次按顺序取出s中的一个字母 可以加在新串的开头 或者结尾  问有多少种方法让新串的前缀是t

如果要让新串的前缀是t串的话 那么s显然要通过某种方式和t串进行一个匹配 通过题意知道s串在取字母的时候可以放新串的前面或者后面 那我们就根据这个模式匹配

对于s串 我们枚举每个字母 对于t串我们枚举一个区间  显然要匹配成功的话 得满足 i = r-l+1

这其实就相当于把两个串进行区间dp了 我们用dp[l][r] 表示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 符号题意)

最终答案:\sum_{i=m}^{n} dp[1][i] 因为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;
}
原创文章 85 获赞 103 访问量 2501

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/105572763