1. 程式人生 > >[DP][NOIP2015]子串

[DP][NOIP2015]子串

正整數 一行 LG == getc P20 公共子串 相等 etc

子串

題目描述

有兩個僅包含小寫英文字母的字符串 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 取模 的結果。

樣例輸入

6 3 1
aabaab
aab

樣例輸出

2

提示

技術分享圖片

技術分享圖片

題解:

從網上ctrl c + v來的你們不會介意的對吧(逃~

“*那麽記一下思路吧,這道題是要壓縮的,它會卡空間的,要滾動數組。
我們按照三維的來考慮,我們再記一個數組f[i][j][k]為選擇第i位後的a串前i個b串前j個選擇k個子串有幾種組合方式
s[i][j][k]是a串前i個b串前j個選擇k個子串有幾種組合方式,f,s數組的差別是一個選了第i個,一個不一定選了第i個

然後和最長公共子串一樣
f數組的遞推思路:要是a的第i位能夠和b的第j位匹配上,我們選擇第i位當一個串是一種情況,
這個時候我們把s數組的s[i-1][j-1][k-1]轉移過來就可以了,那麽i-1顯然也要和j-1匹配上才能多加上額外的一些情況,
如果i-1和j-1都匹配不上就不能再往左延伸了,所以如果a[i]!=b[j]相當於一個公共子串被切斷一樣,f[i][j][k]=0
所以:
f[i][j][k]=f[i-1][j-1][k]+s[i-1][j-1][k-1] (a[i]==b[j])
f[i][j][k]=0 (a[i]!=b[j])
s數組的遞推思路:當a[i]==b[j]時,我們可以選i也可以不選,我們加上f數組就好了和不選的情況s[i-1][j][k]就可以了,
如果不相同那就肯定不選了,此時f數組為0,我們無需特判
s[i][j][k]=f[i][j][k]+s[i-1][j][k]
壓縮的思路:由於我們的每次i都只與i-1有關,所以我們可以把第一維壓縮掉,因為後面的j要用到j-1的情況,
所以我們從後往前更新,k同理,也是從後往前,然後要控制範圍是min(K,j)*”

代碼:

 1 #include<algorithm>
 2 #include<cstdio>
 3 
 4 int n, m, k;
 5 long long f[2][205][205], s[2][205][205];
 6 char a[1005], b[205];
 7 const int mod = 1000000007;
 8 
 9 int read(){
10     int x = 0, f = 1; 
11     char ch = getchar(); 
12     while (ch < 0 || ch > 9) {
13         if (ch == -) {
14             f = -1; 
15         }
16         ch = getchar(); 
17     }
18     while (ch >= 0 && ch <= 9) {
19         x = x * 10 + ch - 0; 
20         ch = getchar(); 
21     }
22     return x * f; 
23 }
24 
25 int main(){
26     n = read(); 
27     m = read(); 
28     k = read(); 
29     scanf("%s", a + 1);
30     scanf("%s", b + 1);
31     int now = 1, last = 0;
32     f[0][0][0] = 1;
33     for (int i = 1; i <= n; i++) {
34         f[now][0][0] = 1;
35         for (int j = 1; j <= m; j++) {
36             for (int kk = 1; kk <= k; kk++) {
37                 if (a[i] == b[j]) {
38                     s[now][j][kk] = (s[last][j - 1][kk] + f[last][j - 1][kk - 1]) % mod;
39                 }
40                 else {
41                     s[now][j][kk] = 0;
42                 }
43                 f[now][j][kk] = (f[last][j][kk] + s[now][j][kk]) % mod;
44             }
45         }
46         std::swap(now, last);
47     }
48     printf("%lld\n", f[last][m][k]);
49     return 0;
50 }

[DP][NOIP2015]子串