洛谷P2679 子串
阿新 • • 發佈:2019-05-02
一個 需要 register 方程 line name .org lse algorithm
傳送門
分析:
首先發現,為了保證無後效性的一位一位往後推,我們需要記錄當前推到 $ a $ 串的哪一個位置了;接著還有記錄匹配了 $ b $ 串的那幾個字符。因為是按照原串順序,所以相當於是即匹配 $ b $ 串的前幾個字符。有這些還不夠,我們還要記錄劃分了幾個子串。最後,為了便於轉移,我們還要標記一維 $ 0/1 $ 狀態,表示 $ a $ 串中的第 $ i $ 個字符是否選入。
這樣,我們就設計好了狀態。我們記 $ f_{i,j,p,v} $ 表示到 $ a $ 串的第 $ i $ 個位置為止使用 $ p $ 個子串匹配 $ b $ 串前 $ j $ 位字符且第 $ i $ 個位置選或不選 $ (v) $ 的方案數。
我們分情況考慮:
$ 1. $ 當 $ a_i=b_j $ 時:
①$ f_{i,j,p,0} $ :由於這位不選,所以就是前面一位選和不選方案數之和,即 $ f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1} $ 。
② $ f_{i,j,p,1}=f_{i-1,j-1,p,1}+f_{i-1,j-1,p-1,0}+f_{i-1,j-1,p-1,1} $ 。
$ 2 . $ 當 $ a_i\ne b_j $ 時:
①不選情況同上,即 $ f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1} $ 。
②由於選不了,自然就是 $ 0 $ ,即 $ f_{i,j,p,1}=0 $ 。
觀察轉移方程,發現每次轉移只用到了前一位!於是我們把第一維很愉快地滾掉了。這樣,空間復雜度就保證是 $ O(mk) $ 了。那麽時間呢?時間是 $ O(n\cdot mk) $ ,但是時間不像空間,這個復雜度是可以接受的。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> #define re register #define mod 1000000007 using namespace std ; const int maxn = 1005 ; const int maxm = 210 ; int n , m , k ; char a[maxn] , b[maxm] ; bool val = true ; int f[2][maxm][maxm][2] ; //f[i][j][p][v]:表示a串的第i個位置為止使用p個子串匹配b串的前j位字符且第i個位置選還是不選(v)的方案數 inline void dp() { f[0][0][0][0] = f[1][0][0][0] = 1 ; for(re int i = 1 ; i <= n ; ++ i , val ^= 1) for(re int j = 1 ; j <= m ; ++ j) for(re int p = 1 ; p <= k ; ++ p) { if(a[i] == b[j]) { f[val][j][p][0] = (f[val ^ 1][j][p][0] + f[val ^ 1][j][p][1]) % mod ; f[val][j][p][1] = (f[val ^ 1][j - 1][p][1] + (f[val ^ 1][j - 1][p - 1][0] + f[val ^ 1][j - 1][p - 1][1]) % mod) % mod ; } else { f[val][j][p][0] = (f[val ^ 1][j][p][0] + f[val ^ 1][j][p][1]) % mod ; f[val][j][p][1] = 0 ; } } } int main () { scanf("%d%d%d" , &n , &m , &k) ; scanf("%s%s" , a + 1 , b + 1) ; dp() ; printf("%d\n" , (f[n & 1][m][k][0] + f[n & 1][m][k][1]) % mod) ; return 0 ; }
洛谷P2679 子串