子串

子串

洛谷真题传送门

题意描述:有A字符串,B字符串,求把A串中k个非空子串合并成B串的方案书。

题解:
我想起洛谷动规专题提高组还是省选那里有写着:到这里的动态规划,往往思考深度比代码长度要大多了。的确就是这样,这道题 ……代码诚然不长,但是,四位深度很深。初测70.
——————————————废话分割线——————————————
很容易(?)想到一个方程

f[i][j][k]=f[i-1][j-1][k-1]+f[i-1][j-1][k]{条件,A[i]=B[j]}

解释:当前字符选用时,能匹配时,方案数为:单独使用当前字符为一个子串 + 与前面相连形成一个子串;

但其实并没有考虑当前字符不选用这种情况,所以要在后面考虑。
所以,可以设
设s[ i ][ j ][ k ]为A用到了 i ,B用到了 j ,已经用了 k 个子串, 并且一定用了当前字符(A[i])时的方案数。
设f[ i ][ j ][ k ]为A用到了 i ,B用到了 j ,已经用了 k 个子串, 无论用不用当前字符(A[i])时的方案数总和。
接下来这个转移可就有蛮难想了。
一个一个来,
先分析一下 s 的转移。
能转移的前提自然是 A[ i ] == B [ j ]啦。
既然 A[i] 一定要用,那么依旧是两种情况:独自成一串 或 与前面的成一串。
独自成一串,方案数为:f[ i-1 ][ j-1 ][ k-1]
与前方共成一串,方案数为:s[ i-1 ][ j-1 ][ k ],因为前一个字符串(A[i-1])也一定要用!
所以合并一下: s[ i ][ j ][ k ] = f[ i-1 ][ j-1 ][ k-1 ] + s[ i-1 ][ j-1 ][ k ];
接着分析 f 的转移。
f[ i ][ j ][ k ] 的来源也有两种: 使用当前字符 或 不使用当前字符
对于使用当前字符,方案数算法如上,答案即:s[ i ][ j ][ k ];
对于不使用当前字符,则从f[ i-1 ]转来,即:f[ i -1 ][ j ][ k ];
合并一下: f[ i ][ j ][ k ] = f[ i-1 ][ j ][ k ] + s[ i ][ j ][ k ];
所以将两个合并一下子,就行了
答案存在f[ n ][ m ][ k ]中,显然边界条件为 f[ i ][ 0 ][ 0 ] = 1;

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1000000007;
int f[2][210][210],sum[2][210][210];
char s1[1010],s2[210];
int k,n,m;
int main(){
  freopen("substring.in","r",stdin);
  freopen("substring.out","w",stdout);
  scanf("%d%d%d",&n,&m,&k);
  scanf("%s",s1+1);scanf("%s",s2+1);
//  printf("string 1:%s   string 2:%s",s1,s2);
//  printf("945294659");
//  return 0;
  sum[0][0][0]=1;
  for(int i=1;i<=n;i++){
    sum[i%2][0][0]=1;
    for(int j=1;j<=m;j++){
      for(int p=1;p<=k;p++){
        if(s1[i]==s2[j]){
          f[i%2][j][p]=(sum[1-(i%2)][j-1][p-1]+f[1-(i%2)][j-1][p])%mod;
        }else f[i%2][j][p]=0;//这一步太关键了,因为数组在滚动,你不能让前面的值影响到现在
        sum[i%2][j][p]=(sum[1-(i%2)][j][p]+f[i%2][j][p])%mod;
      }
    }
  }
  printf("%d",sum[n%2][m][k]);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/beautiful_cxw/article/details/81205303