【GDOI2018模拟7.10】C 动态规划

92 篇文章 1 订阅

一套题目考两道DP有意思?= =

题意略。

一开始其实想的接近正解了,但是后面就歪了,莫名其妙搞到30分= =。。
%%%world_wide_D,AK大佬,瑟瑟发抖。

一开始先把dp[i][j]预处理出来,表示A的前i位和B的前j位的最长公共子序列长度。
然后设f[i][j]表示A的前i位和B的前j位中长度为dp[i][j]的序列的出现次数。
那么求出dp以后明显有两种转移:
不选择i:f[i][j]+=f[i-1][j](dp[i-1][j]==dp[i][j])
选择i:f[i][j]+=f[i-1][p-1]
p为B串前j 个字符中最靠后的与A[i]相同的字符的位置,
若dp[i− 1][p− 1] + 1 == dp[i][j],则f[i][j]+=f[i-1][p-1]最终的答案即为f[n][m]。
这个应该还是挺好理解的,因为从p到j这一段的f都是相同的,所以预处理出p以后直接更新就好了。


#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e3+5;
const int mo=1e9+7;
int dp[N][N],f[N][N],g[N][30];
int n,m;
int pos[N];
char s1[N],s2[N];
int main()
{
    scanf("%s",s1+1);
    scanf("%s",s2+1);
    n=strlen(s1+1);
    m=strlen(s2+1);
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            if (s1[i]==s2[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
        }
    }
    memset(pos,-1,sizeof(pos));
    fo(i,1,m)
    {
        pos[s2[i]-'a']=i;
        fo(j,0,25)g[i][j]=pos[j];
    }
    fo(i,0,n)f[i][0]=1;
    fo(i,0,m)f[0][i]=1;
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            f[i][j]=0;
            int p=g[j][s1[i]-'a'];
            if (dp[i-1][j]==dp[i][j])f[i][j]=(f[i][j]+f[i-1][j])%mo;
            else if (p!=-1&&dp[i-1][p-1]+1==dp[i][j])
                f[i][j]=(f[i][j]+f[i-1][p-1])%mo;
        }
    }
    printf("%d\n",f[n][m]);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值