• 【noip2015】子串


    题目描述

    有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出的位置不同也认为是不同的方案。


    输入

    第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问 题描述中所提到的 k,每两个整数之间用一个空格隔开。第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。


    输出

    输出共一行,包含一个整数,表示所求方案数。(由于答案可能很大,所以这里要求输 出答案对 1,000,000,007 取模的结果。)


    样例输入

    6 3 1
    aabaab
    aab


    样例输出

    2

    对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。



    题解

    好难啊,还好没在15年考。

    解1:

    设 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[ i ] 时,可以是A[ i ] 单独一段,也可以是A[ i ] 与前面的共成一段。

    A[ i ] 单独一段:s[ i ][ j ][ k ] = f[ i-1 ][ j-1 ][ k-1 ]

    A[ i ] 与前面共成一段: s[ i ][ j ][ k ] = s[ i-1 ][ j-1 ][ k ]

    所以: s[ i ][ j ][ k ] = f [ i-1 ][ j-1 ][ k-1 ] + s[ i-1 ][ j-1 ][ k ]

    当A[ i ] != B[ i ] 时,s[ i ][ j ][ k ] = 0

    再考虑 f 的转移:

    f 的转移也有两种情况,

    选择A[ i ] :f [ i ][ j ][ k ] = s[ i ][ j ][ k ]

    不选择A[ i ] :f [ i ][ j ][ k ] = f [ i-1 ][ j ][ k ]

    所以: f [ i ][ j ][ k ] = s[ i ][ j ][ k ] + f [ i-1 ][ j ][ k ]

    答案为 f [ n ][ m ][ k ] 。

    由于数据较大,所以把第一维滚掉。

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    
    const int maxn=1000+50;
    const int maxm=200+50;
    const int mod=1000000007;
    
    int n,m,k,s[2][maxm][maxm],f[2][maxm][maxm],cur;
    char a[maxn],b[maxm];
    
    template<typename T>void read(T& aa){
        char cc; ll ff;aa=0;cc=getchar();ff=1;
        while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
    
    int main(){
        read(n),read(m),read(k);
        cin>>a+1>>b+1;
        f[0][0][0]=1;
        for(int i=1;i<=n;i++){
            cur^=1;
            f[cur][0][0]=1;
            for(int j=1;j<=m;j++)
            for(int w=1;w<=k;w++){
                if(a[i]==b[j]) s[cur][j][w]=(s[cur^1][j-1][w]+f[cur^1][j-1][w-1])%mod;
                else s[cur][j][w]=0;
                f[cur][j][w]=(s[cur][j][w]+f[cur^1][j][w])%mod;
            }
        }
        cout<<f[cur][m][k];
        return 0;
    }

    解2:

    四维dp,dp[ i ][ j ][ k ][ 0/1 ]表示A数组选到了 i ,B数组选到了 j,用了 k 段,选不选A[ i ] 的方案数。和方法1的转移的方式大体一样,初始化dp[ i ][ 0 ][ 0 ][ 0 ] = 1 。

    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    
    const int maxn=1000+50;
    const int maxm=200+50;
    const int mod=1000000007;
    
    int n,m,k,dp[2][maxm][maxm][2],cur;
    char a[maxn],b[maxm];
    
    template<typename T>void read(T& aa){
        char cc; ll ff;aa=0;cc=getchar();ff=1;
        while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
        if(cc=='-') ff=-1,cc=getchar();
        while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
        aa*=ff;
    }
    
    int main(){
        read(n),read(m),read(k);
        cin>>a+1>>b+1;
        dp[0][0][0][0]=1;
        for(int i=1;i<=n;i++){
            cur^=1;
            dp[cur][0][0][0]=1;
            for(int j=1;j<=m;j++)
            for(int t=1;t<=k;t++){
                if(a[i]==b[j]) dp[cur][j][t][1]=((dp[cur^1][j-1][t][1]+dp[cur^1][j-1][t-1][1])%mod+dp[cur^1][j-1][t-1][0])%mod;
                else dp[cur][j][t][1]=0;
                dp[cur][j][t][0]=(dp[cur^1][j][t][0]+dp[cur^1][j][t][1])%mod;
            }
        }
        cout<<(dp[cur][m][k][1]+dp[cur][m][k][0])%mod<<endl;
        return 0;
    }
  • 相关阅读:
    Python中引用自定义类的方法
    使用js判断a是不是NaN 类型
    实现小数保留并四舍五入
    C# 生成全球唯一标识符GUID
    VS2008 激活
    Android 获取当前IP地址
    Android 双屏异显的实现
    用友系统的本币和原币
    .net core Json字符串的序列化和反序列化通用类源码,Newtonsoft和DataContractJsonSerializer性能对比
    建议收藏备用:.net core使用QRCoder生成普通二维码和带Logo的二维码详细使用教程,源码已更新至开源模板
  • 原文地址:https://www.cnblogs.com/rlddd/p/9535479.html
Copyright © 2020-2023  润新知