• 洛谷 P2679 子串 【dp神题】【滚动数组】【2015 noip d2t2】


     偷个懒,题解看这里:https://www.luogu.org/problemnew/solution/P2679

    看作者 GuessYCB  写的

    ===2018.9.6===

    这一次算理解深一点了,也不再觉得这题是神题了,只是思维难度稍微高一点。

    首先定义状态dp[i][j][k]代表A里前i个字符挑出k个子串组成b的前j个字符的总方案数,那答案就是dp[n][m][k];

    然后想怎么转移

    1.a[i]==b[j]

    dp[i][j][k]=dp[i-1][j-1][k-1] + dp[i-1][j-1][k] + dp[i-1][j][k]

    考虑a[i]可以自己一个人组成子串,跟前面的a[i-1]想连组成子串和不选a[i]

    2.a[i]!=b[j]

    dp[i][j][k]=dp[i-1][j][k] (那就是不选a[i])

    【乍一看是对的,但实际上【跟前面的a[i-1]相连组成子串的方案数时】用dp[i-1][j-1][k]表达不对】

    因为这种情况要求a[i-1]必须被选中,但dp[i-1][j-1][k]指的是从A的前i-1个字符中挑出k个子串组成B的前j-1个字符,并没有规定一定要选a[i-1]。所以想到要再

    多加一个状态0/1表示选不选a[i],另一种实现方式就是用我代码里的s数组。

    那转移方程就是

    1.a[i]==b[j]

    dp[i][j][k][0]=dp[i-1][j][k][1](上一个用)+dp[i-1][j][k][0](上一个还不用)

    dp[i][j][k][1]=dp[i-1][j-1][k][1](跟上一个连成一串) + dp[i-1][j-1][k-1][1](小团体,上一个用) + dp[i-1][j-1][k-1][0](小团体,上一个不用)

    2.a[i]!=b[j]

    dp[i][j][k][0] = dp[i-1][j][k][1] + dp[i-1][j][k][0] 因为不能就不选a[i]所以不受影响

    dp[i][j][k][1] = 0 选不了

    dp[i][j][k][0]这个转移的方式有点像前缀和,因为不选a[i]的话,那方案数就是选所有可能的字符k(k在1-i-1之间),把他当作第一个加入k子串里的字符。

    k不一样对应的方案一定不一样,所以不会重复,就是前缀和。

    我说的这个转移方程其实跟我代码里写的转移方程不一样,两者都是对的。。

     1 #include<iostream>
     2 #include<map>
     3 using namespace std;
     4 
     5 int n,m,k;
     6 char A[1005],B[205]; 
     7 long long mod = 1000000007;
     8 
     9 long long f[2][205][205],s[2][205][205];// s[i][j][k]代表A里前i个挑k个子串 拼成B里前j个的方案数,一定用a[i]
    10                                   // f[i][j][k]可用可不用a[i] 
    11                                     //滚动数组消掉i 
    12         // f[i][0][0]=1
    13 int main(){
    14     cin>>n>>m>>k;
    15     for(int i=1;i<=n;i++) cin>>A[i];
    16     for(int i=1;i<=m;i++) cin>>B[i];
    17     
    18     int now=1,last=0;
    19     f[0][0][0]=1;
    20     for(int i=1;i<=n;i++){
    21         f[now][0][0]=1;
    22         for(int j=1;j<=min(i,m);j++){
    23             for(int p=1;p<=min(j,k);p++){//目标每一个状态都有意义
    24                 if( A[i]==B[j] ) s[now][j][p] =  (f[last][j-1][p-1]+s[last][j-1][p])%mod ;
    25                 else s[now][j][p]=0;
    26                 f[now][j][p] =  (s[now][j][p]+f[last][j][p])%mod;
    27             }
    28         }
    29         now=!now; last=!last;
    30     }
    31     
    32     cout<<f[last][m][k];
    33     
    34     return 0;
    35 }
  • 相关阅读:
    接口-DAO模式代码阅读及应用
    3.1-有理数类的设计

    树、二叉树、查找算法总结
    编辑器、编译器、文件、IDE等常见概念辨析
    二叉排序树
    markdown的一些语法
    数据结构小结(线性表)
    springMVC model传对象数组 jq 获取
    java.sql.SQLException: Data truncated for column 'lastSeason' at row 1
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/9251188.html
Copyright © 2020-2023  润新知