題目背景
無題目描述
有兩個僅包含小寫英文字母的字符串 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]
輸入輸出樣例
6 3 1 aabaab aab
2
6 3 2 aabaab aab
7
6 3 3 aabaab aab
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題,然后來想想怎么表示狀態,對答案有影響的就是A串的第i個字符,B串的第j個字符,和k個子串,簡單來說就是和選取的字符和子串的數量有關.那么設f[i][j][kk]表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數.求方案數可以采用加法原理,考慮A串的第i個字符,那么這個字符的決策只有取或不取,很明顯,加法原理,把不取的方案數和取的方案數加起來就可以,但是狀態的定義並不能看出這個字符到底取不取,或者說並不能推出結果來,怎么辦呢?
那么就用一個數組s[i][j][kk]來表示在A串的前i個字符中選kk個子串匹配B串的前j個字符的方案數,A串的第i個字符會被取到.那么這個s數組該怎么推出來呢?可以發現,如果取第i個字符也有2種可能,因為kk是一定的,第i個字符可能和第i-1個字符合並成一個子串,那么從s[i-1][j][kk]轉移過來,也可能不和第i-1個字符合並成一個子串,那么就要新開一個子串,故kk一定從kk-1轉移過來,根據加法原理,那么s[i][j][kk] = s[i-1][j-1][kk] + f[i-1][j-1][kk-1].
還有一個問題,f數組該怎么推呢?根據之前分析的,把不取和取的加上來即可,即f[i][j][kk] = s[i][j][kk] + f[i-1][j][kk],其實可以發現,f數組就相當於前綴和數組.
還有一個問題:這是一個三維的狀態轉移方程!空間不一定開的下,再看數據范圍,這絕對MLE,怎么辦?注意到i只能從i或i-1轉移過來,可以想到滾動數組,滾動數組有一個比較好些的寫法就是把第一維全部加上&1,或者干脆弄兩個變量處理完一組數據就交換即可.
本蒟蒻水平很低,如果神犇發現了錯誤,望指出QAQ
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n, m, k; long long f[2][205][205], s[2][205][205]; char a[1005], b[205]; const int mod = 1000000007; int main() { scanf("%d%d%d", &n, &m, &k); scanf("%s", a + 1); scanf("%s", b + 1); int now = 1, last = 0; f[0][0][0] = 1; for (int i = 1; i <= n; i++) { f[now][0][0] = 1; for (int j = 1; j <= m; j++) for (int kk = 1; kk <= k; kk++) { if (a[i] == b[j]) s[now][j][kk] = (s[last][j - 1][kk] + f[last][j - 1][kk - 1]) % mod; else s[now][j][kk] = 0; f[now][j][kk] = (f[last][j][kk] + s[now][j][kk]) % mod; } swap(now, last); //一定要變,否則就是一直自己跟自己轉移. } printf("%lld\n", f[last][m][k]); return 0; }
