[NOIP2015]子串 题解

题目描述

有两个仅包含小写英文字母的字符串A和B。

现在要从字符串A中取出k个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?

注意:子串取出的位置不同也认为是不同的方案。

输入格式

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

第二行包含一个长度为 n 的字符串,表示字符串A。 

第三行包含一个长度为 m 的字符串,表示字符串B。

输出格式

输出共一行,包含一个整数,表示所求方案数。

由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。

数据范围


$ 1 le n le 1000, \\ 1 le m le 200,\\ 1 le k le m\\ $

$Solution:$

只要看出来是dp就很好做了。

设$dp[i][j][k][0/1]$表示匹配到a的第i位,b的前j位已完成匹配,用了k个子串,a的当前位选没选的方案数。

首先考虑这位不选的情况,显然有$dp[i][j][k][0]=dp[i-1][j][k][0]+dp[i-1][j][k][1]$。上一位选不选皆可。

如果要选这一位,首先必须保证$a_i=b_j$,那么你既可以在上一位也选的情况下沿用之前的那个串,又可以新开一个串,而新开一个串和上一位选不选没什么关系,所以三者相加。

滚动数组。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1005;
int n,m,K;
char a[N],b[N];
ll dp[2][202][202][2];
int main()
{
    scanf("%d%d%d",&n,&m,&K);
    scanf("%s",a+1);scanf("%s",b+1);
    dp[0][0][0][0]=dp[1][0][0][0]=1;
    int now=0,pre=1;
    for(int i=1;i<=n;i++)
    {
        now^=1,pre^=1;
        //memset(dp[now],0,sizeof(dp[now]));
        for(int j=1;j<=m;j++)
        {
            for(int k=1;k<=K;k++)
            {
                if(a[i]==b[j])
                    dp[now][j][k][1]=(dp[pre][j-1][k-1][0]+dp[pre][j-1][k][1]+dp[pre][j-1][k-1][1])%mod;
                else dp[now][j][k][1]=0;
                dp[now][j][k][0]=(dp[pre][j][k][0]+dp[pre][j][k][1])%mod;
            }
        }
    }
    ll ans=(dp[now][m][K][0]+dp[now][m][K][1])%mod;
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Rorschach-XR/p/11670766.html