NOIP2015子串[序列DP]

Candy?發表於2016-08-28

題目背景

無 

題目描述

有兩個僅包含小寫英文字母的字串 A 和 B。現在要從字串 A 中取出 k 個互不重

疊的非空子串,然後把這 k 個子串按照其在字串 A 中出現的順序依次連線起來得到一 個新的字串,請問有多少種方案可以使得這個新串與字串 B 相等?注意:子串取出 的位置不同也認為是不同的方案。

輸入輸出格式

輸入格式: 

輸入檔名為 substring.in。

第一行是三個正整數 n,m,k,分別表示字串 A 的長度,字串 B 的長度,以及問

題描述中所提到的 k,每兩個整數之間用一個空格隔開。 第二行包含一個長度為 n 的字串,表示字串 A。 第三行包含一個長度為 m 的字串,表示字串 B。

 

輸出格式:

 

輸出檔名為 substring.out。 輸出共一行,包含一個整數,表示所求方案數。由於答案可能很大,所以這裡要求[b]輸出答案對 1,000,000,007 取模的結果。[/b]

 

輸入輸出樣例

輸入樣例#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。

-----------------------------------------------------

兩種思路

1.f[i][j][k][0/1] a的前i,b的前j,用了k個串,0代表所有方案,1代表必須用第i個的方案

陣列開不下,考慮把i滾掉,倒序列舉j和k或者用now和pre交替

WARN:煞筆的把swap(i,j)寫到了最內層,因為它查了半個多小時的錯

2.不用0/1,match[i][j]表示ij最多往前匹配幾個,對f字首和優化(我還是有點玄乎)

//
//  main.cpp
//  noip2015子串
//
//  Created by abc on 16/8/28.
//  Copyright © 2016年 abc. All rights reserved.
//

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1005,M=205,K=205,MOD=1000000007;
int n,m,p;
char a[N],b[M];
int f[2][M][K][2],pre=1,now=0,ans=0;
void dp(){
    f[pre][0][0][0]=f[now][0][0][0]=1;
    for(int i=1;i<=n;i++,swap(now,pre))
        for(int j=1;j<=m;j++)
            for(int k=1;k<=p;k++){
                if(a[i]==b[j]) f[now][j][k][1]=(f[pre][j-1][k-1][0]+f[pre][j-1][k][1])%MOD;
                else f[now][j][k][1]=0;
                f[now][j][k][0]=(f[pre][j][k][0]+f[now][j][k][1])%MOD;
                //printf("%d %d %d  %d %d\n",i,j,k,f[now][j][k][1],f[now][j][k][0]);
            }
            
}
int main(int argc, const char * argv[]) {
    scanf("%d%d%d%s%s",&n,&m,&p,a+1,b+1);
    dp();
    cout<<f[pre][m][p][0];
    
    return 0;
}

 

相關文章