洛谷 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 }
原文地址:https://www.cnblogs.com/ZhenghangHu/p/9251188.html