• [NOIP2016TG] 洛谷 P2679 子串


    题意

    从字符串(A)中取出(k)个互不重叠的非空子串,顺次连接起来得到一个新的字符串B,求方案总数。注意:子串取出的位置不同也认为是不同的方案。答案对(10^9+7)取模。

    (A)长度为(m),(B)长度为(n),所有数据满足

    (1leq nleq 1000,1leq mleq 200,1leq kleq m)

    思路

    后缀自动机之类的玄学做法,即使可以过,也不适合蒟蒻在(NOIP)赛场上拿出来。这个数据范围也不适合(不记忆化的)搜索。很像(DP),但是要怎样(DP)呢?

    按照(DP)的常规思路,先给(m)(n)(k)分别设置一个维度,记为

    (f[i][j][p])

    表示转移到(A[i])(B[j]),使用了(p)个子串。一种思路是枚举(A)数组的([1,i])段再匹配。但是这不太好理解,可以人为地再增加一维布尔变量,相当于把一个状态分为两部分,其意义是(A[i])是否对应(B[i])。则有

    ​ $f[i][j][p][q],qin {0,1} $

    状态转移

    显然,决定转移方程的条件是(A[i]==B[j])

    如果(q=0),无论(A[i])(B[j])相等与否,都直接照搬(A[i-1])(B[j])的转移结果即可

    (f[i][j][p][0]=f[i-1][j][p][0]+f[i-1][j][p][1])

    如果(q=1),则分类讨论。

    • (A[i]==B[j])时,答案是(A[i-1])(B[j-1])的方案数。这也分为两种,一种是把(A[i])(B[j])作为单独的子串,此时(p)要减一;一种是把子串(A[i])(B[j])(A[i-1])(B[j-1])相连,此时(p)保持不变,(q)只能是(1)
    • (A[i]!=B[j])时,无法匹配,答案为(0)

    综上所述,得

    (f[i][j][p][0]=f[i-1][j][p][0]+f[i-1][j][p][1])

    (f[i][j][p][1]=egin{cases}{A[i]==B[j]quad f[i-1][j-1][p-1][1]+f[i-1][j-1][p-1][0]+f[i-1][j-1][p][1]}\{A[i]!=B[j]quad 0}end{cases})

    还有一个问题,初值怎么设?

    可以在第一层循环内加判断。如果(A[i])(B[1])相同,则

    (f[i][1][1][1]=1,f[i][1][1][0]=count,count++)

    优化

    空间大约是

    (1000*200*200*2=8 imes 10^7)

    这谁顶得住啊。观察发现,每次状态转移只和上一次结果有关。可以使用滚动数组优化。

    代码

    #include<iostream>
    #include<cstring>
    using namespace std;
    int f[2][205][205][2],n,m,k,cnt,i=1;
    string a,b;
    const int mod=1000000007;
    int main()
    {
        cin>>n>>m>>k;
        cin>>a>>b;
        a=' '+a,b=' '+b;
        for(int temp=1;temp<=n;temp++,i^=1)
        {
            f[i][1][1][0]=cnt;
            if(a[temp]==b[1]) f[i][1][1][1]=1,cnt++;
            else f[i][1][1][1]=0; 
            for(int j=2;j<=m;j++)
                for(int p=1;p<=k;p++)
                {
                    f[i][j][p][0]=(f[i^1][j][p][1]+f[i^1][j][p][0])%mod;
                    f[i][j][p][1]=(a[temp]==b[j])*((f[i^1][j-1][p-1][1]+f[i^1][j-1][p-1][0])%mod+f[i^1][j-1][p][1])%mod;
                }
        }
        cout<<(f[n&1][m][k][1]+f[n&1][m][k][0])%mod;
        return 0;
    }
    
  • 相关阅读:
    手机版页面跳转
    设计模式转载(史上最全设计模式导学目录|完整版)
    linux 常用命令
    数据结构与算法JavaScript描述.
    常用算法js版(冒泡排序 ,选择排序 ,插入排序 ,希尔排序 ,归并排序 ,快速排序 ,堆排序 ,计数排序 ,桶排序 ,基数排序)
    伪元素和伪类
    双向数据绑定
    2016年黑马程序员已出品各学科最新学习路线图:
    jQuery插件开发全解析
    蛋白质、碳水化合物和脂肪
  • 原文地址:https://www.cnblogs.com/ehznehc/p/10897159.html
Copyright © 2020-2023  润新知