洛谷P2679 子串
传送门
分析:
首先发现,为了保证无后效性的一位一位往后推,我们需要记录当前推到 $ 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 ;
}
顺风不浪,逆风不怂。