[2015NOIP] 子串

子串
【问题描述】
有两个仅包含小写英文字母的字符串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

由于子串按序依次连接 考虑dp
方程 dp[i][j][k]=dp[i][i-1][k] (A[i]!=B[j])
dp[i][j][k]=dp[i-1][j][k]+dp[i-1][j-1][k]+dp[i-1][j-1][k-1]-dp[i-2][j-1][k] (A[i]==B[j])

/*by dudu 2016-6-8 substring*/
#include<iostream>
#include<cstdio>
using namespace std;

#define P 1000000007
int N,M,K,cur;
char A[1010],B[510];
int dp[2][1010][510];

int main()
{
    //freopen("2015substring.in","r",stdin),freopen("2015substring.out","w",stdout);
    scanf("%d%d%d",&N,&M,&K);
    scanf("%s %s",A+1,B+1);
    
    for(int i=0;i<=N;i++)
        dp[0][i][0]=1;
        
    for(int k=1;k<=K;k++)
    {
        cur^=1;
        for(int i=0;i<=N;i++)
            dp[cur][i][k-1]=0;
            
        for(int j=k;j<=M;j++)
        {
            for(int i=j;i<=N;i++)
            {
                if(A[i]==B[j])
                {
                    dp[cur][i][j]=(dp[cur][i-1][j]+dp[cur][i-1][j-1])%P;
                    dp[cur][i][j]=(dp[cur][i][j]+dp[cur^1][i-1][j-1])%P;
                    if(i>=2) dp[cur][i][j]=(dp[cur][i][j]-dp[cur][i-2][j-1]+P)%P;
                }
                else dp[cur][i][j]=dp[cur][i-1][j];
            }
        }
    }    
    cout<<dp[cur][N][M];
    return 0;
}

 

posted @ 2016-06-08 15:56  duduorz  阅读(1320)  评论(0)    收藏  举报