【题解】NOIP 2015 子串

!这题我做了三个月啊

题目描述

有两个仅包含小写英文字母的字符串 \(A\)\(B\)

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

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

输入格式

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

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

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

输出格式

一个整数,表示所求方案数。

由于答案可能很大,所以这里要求输出答案对 \(1000000007\) 取模的结果。

题解

首先有这些变量名:

\(dp[i][j][k]\) : 表示拿\(A[i]\)\(B[j]\)配对,配成\(K\)段的方案数,假如\(A[i]\)\(B[j]\)配不上的话就等于\(0\)
\(sum[i][j][k]\) : 表示\(A\)\(1\)\(i\),能和\(B[j]\)配成\(K\)段的方案总数(\(dp\)数组的前缀和数组),意思就是\(sum[i][j][k] = dp[1][j][k] + dp[2][j][k]...+dp[i][j][k]\)

那么考虑单独的\(A[i]\)\(B[j]\),存在两种情况:

第一种是\(A[i]\)\(B[j]\)配上了,那么就又分出两种情况:

第一种是\(A[i - 1]\)\(B[j - 1]\)也配上了,那么可以直接接在它们后面,也可以另起炉灶(为什么不用考虑\(A[i - 1]\)\(B[j - 1]\)没接上的情况?因为没接上的话\(dp[i - 1][j - 1] = 0\),无需考虑);

第二种是\(A[i - 1]\)\(B[j - 1]\)没配上,那我们就必须找到某个\(A[i']\)\(B[j - 1]\)配上了,而且配成了\(k - 1\)段的地方,让\(A[i]\)\(B[j]\)接在后面配成第\(k\)
然后我们发现,要找到\(A[i']\)\(B[j']\)的位置是要\(O(n)\)处理的,时间复杂度承受不住,所以我们就要用到\(sum\)数组,不用找,直接累加起前一个匹配位置\(A[i']\)的方案数就可以

第二种是配不上\(A[i]\)\(B[j]\)配不上,那么直接赋值为\(0\)

注意:我们的\(dp\)转移方式是用\(A\)数组的每一位去尝试匹配\(B\)的某一位,只有\(B\)的上一位已经匹配过了,这一位才能继续匹配

给大佬代码:

#include<bits/stdc++.h>
using namespace std;
#define rint register int
int n, m, k;
int dp[2][210][210], sum[2][210][210];
char a[1010], b[250];
inline int read( void ){
    int re = 0, f = 1; char ch = getchar();
    while( ch > '9' || ch < '0' ){
        if( ch == '-' ) f = -1;
        ch = getchar();
    }
    while( ch >= '0' && ch <= '9' ){
        re = re * 10 + ch - '0';
        ch = getchar();
    }
    return re * f; 
}
int main( void ){
    n = read(); m = read(); k = read();
    scanf( "%s%s", a+1, b+1 );
    dp[0][0][0] = 1;//一个都不匹配的方案数当然是0
    int now = 0, pre = 1;
    for( rint i = 1; i <= n; i++ ){
        dp[now][0][0]=sum[now][0][0] = 1;
        dp[pre][0][0]=sum[pre][0][0] = 1;
        for( rint j = 1; j <= m; j++ ){
            for( rint kk = 1; kk <= k; kk++ ){
                if( a[i] == b[j] ){
                    dp[now][j][kk] = ( sum[pre][j - 1][kk - 1] + dp[pre][j - 1][kk] ) % 1000000007;
                } 
                else dp[now][j][kk] = 0;
                sum[now][j][kk] = ( sum[pre][j][kk] + dp[now][j][kk] ) % 1000000007; //处理前缀和
            } 
        }
        swap( now, pre ); 
    }
    cout << sum[pre][m][k];
    return 0;
}
posted @ 2019-11-09 22:08  Indigo_Zeppeli  阅读(225)  评论(0编辑  收藏  举报