[NOIP2015]子串

[NOIP2015]子串

状态

首先我们考虑设计状态。我们发现,为了保证无后效性的一位一位往后推,我们需要记录当前推到 a a a 串的哪一个位置了;接着还有记录匹配了 b b b 串的哪几个字符。因为是按照原串顺序,所以相当于是匹配 b b b 的前几个字符。有这些还不够,我们还要记录划分了几个子串。最后,为了便于转移,我们还要标记一维 0 / 1 0/1 0/1 状态,表示 a a a 串中的第 i i i 个字符是否选入。

这样,我们就设计好了状态。我们记 f i , j , p , v f_{i,j,p,v} fi,j,p,v 表示 a a a 串的第 i i i 个位置为止使用 p p p 个子串匹配 b b b 串前 j j j 位字符且第 i i i 个位置选或不选 ( v ) (v) (v)的方案数。

转移

a i = b i a_i=b_i ai=bi
1. f i , j , p , 0 f_{i,j,p,0} fi,j,p,0:由于这位不选,所以就是前面一位选和不选的方案之和,即 f i , j , p , 0 = f i − 1 , j , p , 0 + f i − 1 , j , p , 1 f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1} fi,j,p,0=fi1,j,p,0+fi1,j,p,1
2. f i , j , p , 1 = f i − 1 , j − 1 , p , 1 ( 连 在 第 p 个 子 串 后 边 ) + f i − 1 , j − 1 , p − 1 , 0 + f i − 1 , j − 1 , p − 1 , 1 f_{i,j,p,1}=f_{i-1,j-1,p,1}(连在第p个子串后边)+f_{i-1,j-1,p-1,0}+f_{i-1,j-1,p-1,1} fi,j,p,1=fi1,j1,p,1(p)+fi1,j1,p1,0+fi1,j1,p1,1
因为要连在第 p p p 个子串后边,所以一定由 i − 1 i-1 i1 选了的转移过来
a i ≠ b i a_i \ne b_i ai=bi
1.不选,即 f i , j , p , 0 = f i − 1 , j , p , 0 + f i − 1 , j , p , 1 f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1} fi,j,p,0=fi1,j,p,0+fi1,j,p,1
2.选了就会使得贡献变成 0 0 0,即 f i , j , p , 1 = 0 f_{i,j,p,1} = 0 fi,j,p,1=0

边界: f [ 0 ] [ 0 ] [ 0 ] [ 0 ] = f [ 1 ] [ 0 ] [ 0 ] [ 0 ] = 1 f[0][0][0][0] = f[1][0][0][0] = 1 f[0][0][0][0]=f[1][0][0][0]=1

优化空间

空间大小: 1000 × 200 × 200 × 2 = 8 × 1 0 7 1000\times200\times200\times2=8\times10^7 1000×200×200×2=8×107
所以我们观察转移方程,发现每次转移只用到了前一位!于是我们把第一维很愉快地滚掉了。这样,空间复杂度就保证是 O ( m k ) O(mk) O(mk) 了。那么时间呢?时间是 O ( n ⋅ m k ) O(n\cdot mk) O(nmk),但是时间不像空间,这个复杂度是可以接受的。于是,完整算法就结束了。

code:

#include<bits/stdc++.h>
#define _ 0
using namespace std;
typedef long long ll;
const int maxn = 5e3 + 9;
const int mod = 1000000007;
ll n, m, ans, k;
bool val = 1;
int f[2][209][209][2];
char a[1009], b[209];
void work()
{
	cin >> n >> m >> k;
	cin >> (a + 1) >> (b + 1);
	f[0][0][0][0] = f[1][0][0][0] = 1;
	for(int i = 1; i <= n; ++i, val ^= 1)
		for(int j = 1; j <= m; ++j)
			for(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;
				}
	cout << (f[n&1][m][k][1] + f[n&1][m][k][0]) % mod << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	//int TT;cin>>TT;while(TT--)
	work();
	return ~~(0^_^0);
}

也可以设状态 f i , j , p , v f_{i,j,p,v} fi,j,p,v 代表字符串 A A A 的前 i i i 个字符,用了 j j j 个不重叠子串,匹配了 B B B 串前 p p p 个字符,第 i i i 位选或没选,一样ac
code:

#include<bits/stdc++.h>
#define _ 0
using namespace std;
typedef long long ll;
const int maxn = 5e3 + 9;
const int mod = 1000000007;
ll n, m, ans, k;
bool val = 1;
int f[2][209][209][2];
char a[1009], b[209];
void work()
{
	cin >> n >> m >> k;
	cin >> (a + 1) >> (b + 1);
	f[0][0][0][0] = f[1][0][0][0] = 1;
	for(int i = 1; i <= n; ++i, val ^= 1)
		for(int j = 1; j <= k; ++j)
			for(int p = 1; p <= m; ++p)
				if(a[i] == b[p])
				{
					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][p-1][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;
				}
	cout << (f[n&1][k][m][1] + f[n&1][k][m][0]) % mod << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	//int TT;cin>>TT;while(TT--)
	work();
	return ~~(0^_^0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值