Noip 2015 子串(洛谷P2679)

来源:互联网 发布:c语言求最大公因数函数 编辑:程序博客网 时间:2024/05/18 01:44

题目描述

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

输入输出格式

输入格式:
输入文件名为 substring.in。

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

输出格式:
输出文件名为 substring.out。 输出共一行,包含一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。
输入输出样例

输入样例#1:
6 3 1
aabaab
aab
输出样例#1:
2

输入样例#2:
6 3 2
aabaab
aab
输出样例#2:
7

输入样例#3:
6 3 3
aabaab
aab
输出样例#3:
7

对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;
对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2;
对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m;
对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m;
对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m;
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。


这是我见过的题中,代码长度与思维难度比例差距最大的一道神题!
代码真的短的惊人,但是思维难度真的不是一般的大。

首先这一看就是一道动态规划吧。

第一眼看到这题的时候,想到的DP式子是这样的:
dp[ i ][ j ][ k ]代表A串位置到了 i ,B串到了 j ,已经用了 k 个子串。
自然想到: dp[ i ][ j ][ k ] = dp[ i-1 ][ j-1 ][ k ] + dp[ i-1 ][ j-1 ][ k-1 ]; ( A[i] == B[j] )
即:能匹配时,方案数为:单独使用当前字符为一个子串 + 与前面相连形成一个子串;

稍微仔细一想就可以知道,这个DP式子是有问题的。
如果不使用当前字符,情况是什么样的呢?貌似被默默的遗漏了耶……

所以我们就要分开来设了。
设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 [ j ]啦。
既然 A[i] 一定要用,那么依旧是两种情况:独自成一串与前面的成一串
独自成一串,方案数为:f[ i-1 ][ j-1 ][ k-1]
与前方共成一串,方案数为:s[ i-1 ][ j-1 ][ k ],因为前一个字符串(A[i-1])也一定要用!
所以合并一下: s[ i ][ j ][ k ] = f[ i-1 ][ j-1 ][ k-1 ] + s[ i-1 ][ j-1 ][ k ];

接着分析 f 的转移。
f[ i ][ j ][ k ] 的来源也有两种: 使用当前字符不使用当前字符
对于使用当前字符,方案数算法如上,答案即:s[ i ][ j ][ k ];
对于不使用当前字符,则从f[ i-1 ]转来,即:f[ i -1 ][ j ][ k ];
合并一下: f[ i ][ j ][ k ] = f[ i-1 ][ j ][ k ] + s[ i ][ j ][ k ];

所以将两个合并一下子,就得到:

-->对于每一个i , j , k :if( A[i] == B[j] )s[i][j][k] = f[i-1][j-1][k-1] + s[i-1][j-1][k];else s[i][j][k] = 0;f[i][j][k] = f[i-1][j][k] + s[i][j][k];

答案存在f[ n ][ m ][ k ]中,显然边界条件为 f[ i ][ 0 ][ 0 ] = 1;

到这里这道题就做完了。
别看上面说的顺溜溜的,笔者做的时候整个脑子都是混乱的!
当然直接开O(n*m*k)的数组是肯定开不下的,第一维的 i 显然可以滚掉(对于提高组大佬这肯定是so easy 啦)。
下面是具体实现代码:

#include<bits/stdc++.h>#define mod 1000000007using namespace std;int a,b,k;char A[1002],B[1002];int f[3][1000][1000],s[3][1000][1000];int main(){    scanf("%d%d%d\n",&a,&b,&k);    scanf("%s",A+1);  scanf("%s",B+1);    int now=1,past=0;     f[0][0][0]=1;    for(int e=1;e<=a;e++){        f[now][0][0]=1;        for(int j=1;j<=b;j++){            for(int t=1;t<=k;t++){                if(A[e]==B[j])s[now][j][t]=(s[past][j-1][t]+f[past][j-1][t-1])%mod;                else s[now][j][t]=0;                f[now][j][t]=(f[past][j][t]+s[now][j][t])%mod;            }        }swap(now,past);    }    cout<<f[past][b][k];    return 0;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新买的房子漏水怎么办 微信忘记收款了怎么办 线雕隆鼻顶线怎么办 苹果7触屏不灵怎么办 苹果5s键盘太小怎么办 苹果5s按键失灵怎么办 苹果7突然没触摸怎么办 苹果屏幕ic坏了怎么办 苹果6手机触摸屏失灵怎么办 苹果5s屏幕黑了怎么办 乐视手机充电慢怎么办 苹果5s触摸屏失灵怎么办 苹果6出现闪屏怎么办 苹果4s没有卡槽怎么办 苹果se触屏失灵怎么办 苹果6s屏幕乱跳怎么办 6s屏幕触摸失灵怎么办 苹果6sp屏幕失灵怎么办 苹果6s屏幕不动怎么办 苹果6s卡住了怎么办 苹果6s经常卡屏怎么办 苹果6s点不动了怎么办 手机屏自己乱点怎么办 苹果手机摔开了怎么办 苹果屏幕摔开了怎么办 苹果手机6开不了机怎么办 苹果6开不开机怎么办 苹果6s开不开机怎么办 苹果6手机开不了机怎么办 苹果6s不能开机怎么办 苹果6p无法开机怎么办 苹果6s开不了机怎么办 苹果7屏幕划不动怎么办 苹果6老是卡机怎么办 苹果手机黑屏开不了机怎么办 苹果6plus掉水里了怎么办 苹果6黑屏开不了机怎么办 苹果手机6死机了怎么办 苹果7手机死机怎么办啊 苹果7突然死机了怎么办 苹果手机5s死机怎么办