P2679 [NOIP2015 提高组] 子串

原题链接

考察:线性dp

此题不会,fw本f

思路:

       求方案数且需要取模,基本上使用dp求解.

       根据题目,dp状态一定要记录a取到哪一位,b匹配到哪一位,同时还有段数限制,所以还需要记录段数.所以设置f[i][j][k]为以a的前i位匹配了b前j位,已经用了k段.

       此时还发现对于第i位,我们需要判断它是否是与前面构成连续的一段,而且还需要判断第i位是否使用.

       那我们再增加一维f[i][j][k][0/1]表示第i位是否使用,就可以区分是否使用和是否连续的四种情况.

       再考虑初始化,根据dp定义f[0][0][0][0] = 1但也有前i位都不使用的状态因此f[i][0][0][0] = 1

1 if(a[i]==b[j]) f[i][j][k][0] = f[i-1][j][k][0]+f[i-1][j][k][1]           f[i][j][k][1] = f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1]+f[i-1][j-1][k][1]
2 
3 else f[i][j][k][0] = f[i-1][j][k][1]+f[i-1][j][k][0]

      

       如果直接这么写需要1000*200*200*2大概300MB的空间,因此需要空间优化.观察到只需要i和i-1.所以能滚动数组.只需要在此基础上在第i位&1即可.但是注意,当我们不滚动的时候,每次v与t的值都是0,所以滚动的时候需要重新赋值0.

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 typedef long long LL;
 5 const int N = 1010,M = 210,Mod = 1000000007;
 6 int n,m,k,f[2][M][M][2];
 7 char a[N],b[M];
 8 void solve() 
 9 {
10     f[0][0][0][0] = 1;//记录方案的数组滚动需要清零
11     f[1][0][0][0] = 1;
12     for(int i=1;i<=n;i++)
13     {
14         f[i&1][0][0][0] =1;
15         for(int j=1;j<=m;j++)
16           for(int s=1;s<=k;s++)
17           {
18             int& v = f[i&1][j][s][1];
19             int& t = f[i&1][j][s][0];
20             v = t = 0;
21             if(a[i]==b[j])
22             {
23                int val = ((LL)f[i-1&1][j-1][s-1][0]+f[i-1&1][j-1][s-1][1])%Mod;
24                val = ((LL)val+f[i-1&1][j-1][s][1])%Mod;
25                v = ((LL)v+val)%Mod;
26             }
27             t += ((LL)f[i-1&1][j][s][1]+f[i-1&1][j][s][0])%Mod;
28             t%=Mod; 
29             v%=Mod;
30           }
31     }
32 }
33 int main() 
34 {
35     scanf("%d%d%d%s%s",&n,&m,&k,a+1,b+1);
36     solve();
37     int ans = ((LL)f[n&1][m][k][1]+f[n&1][m][k][0])%Mod;
38     printf("%d\n",ans);
39     return 0;
40 }

 

posted @ 2021-04-09 18:46  acmloser  阅读(115)  评论(0编辑  收藏  举报