zoukankan      html  css  js  c++  java
  • [NOIP2015] 子串 题解

    这题目第一眼扫过去就是DP,状态的表示熟练的人也不能想出来用$f[i,j,k]$表示当$A$匹配到前$i$位,$B$字符串匹配到$j$位,将$A$序列分成$k$段时候所能得到的数量。

    总共有两种情况:
    一:$i$这一位不与$j$匹配,即用前$i-1$去匹配$B$的前$j$位;二:$i$这一位与$i$前几位能与$B$的$j$这一位与其前几位一起匹配,作为一个单独的段(也就是有相同的后缀)。那么就有$f[i,j,k]=f[i-1][j][k]$(不匹配)$+sum_{L}f[i-L-1][j-L-1][k-1]$(满足$A[i-L..i]=B[j-L..j]$,也就是所说的一起匹配)。

    那么注意好初始化不难写出一个朴素的代码:

    for(int i=0;i<=n;++i) f[i][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m && j<=i;j++){
            for(int p=1;p<=k && p<=j;p++){
                f[i][j][p]+=f[i-1][j][p];
                for(int l=0;l<i && l<j && A[i-l]==B[j-l];l++)
                    f[i][j][p]+=f[i-l-1][j-l-1][p-1];
            }
        }
    }

    可惜,这样写会挂掉,时间和空间上都是。先考虑时间上的优化,可以优化的地方只剩下循环找一起匹配的段哪里了。我们想如果$A[i-L..i]=B[j-L..j]$的话,那么$f[i-1,j-1,k]$是不是保存了求$sum_{L}f[i-L-1][j-L-1][k-1]$的那一段呢?(因为就相当于$A[i-1..i]$与$B[j-1..j]$一起匹配进去然后算)。

    但是如果这样简单的改一下代码是不行的:

    for(int i=0;i<=n;++i) f[i][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m && j<=i;j++){
            for(int p=1;p<=k;p++){
                f[i][j][p]+=f[i-1][j][p];
                if(A[i]==B[j]){
                    f[i][j][p]+=f[i-1][j-1][p-1];
                       if(A[i-1]==B[j-1])
                           f[i][j][p]+=f[i-1][j-1][p];
                }
            }
        }
    }

    因为这里的$f$数组保存的信息不一定能构成连续,它也会保存前面不能连续匹配的数据,比如这组数据:

    6 3 2
    aabaab
    aab
    按照上面代码跑出来的$f[4,1,1]=3$这本身没错,但是看$f[5,2,2]=f[4,2,2]+f[4,1,0]+f[4,1,1]=1+0+3=4$,显然有误。仔细思考就会发现我们可以新建一个数组$g$,当$A[i]!=B[j]$的时候把$g$置为$0$,当$A[i]=B[j]$的时候令$g[i][j][k]=g[i-1][j-1][k]$(和前面连续的一同构成这第k段)$+f[i-1][j-1][k-1]$(单独构成第k段),而$f[i][j][k]=g[i][j][k]+f[i-1][j][k]$就行了。

    然后就是空间的优化了,像背包一样把第一维省略掉,第二位倒序循环就ok了。

    最后附上代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1005,M=205,mod=1000000007;
    typedef long long ll;
     
    char A[N],B[N];
    ll f[M][M],g[M][M];
    int n,m,k;
     
    int main(){
        scanf("%d%d%d",&n,&m,&k);
        scanf("%s",A+1);
        scanf("%s",B+1);
        f[0][0]=1;
        for (int i=1;i<=n;i++){
            for(int j=min(m,i);j>=1;j--) {
                for(int p=1;p<=k && p<=j;p++) {
                    if(A[i]==B[j])  g[j][p]=(g[j-1][p]+f[j-1][p-1])%mod;
                    else g[j][p]=0;
                    f[j][p]=(g[j][p]+f[j][p])%mod;
                }
            }
        }
        printf("%lld",f[m][k]%mod);
        return 0;
    }
  • 相关阅读:
    hdu 5101 Select
    hdu 5100 Chessboard
    cf B. I.O.U.
    cf C. Inna and Dima
    cf B. Inna and Nine
    cf C. Counting Kangaroos is Fun
    Radar Installation 贪心
    spfa模板
    Sequence
    棋盘问题
  • 原文地址:https://www.cnblogs.com/Asika3912333/p/11753253.html
Copyright © 2011-2022 走看看