Evanyou Blog 彩带

洛谷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 ;
}
posted @ 2019-05-02 16:11  Stephen_F  阅读(145)  评论(0编辑  收藏  举报