状态
首先我们考虑设计状态。我们发现,为了保证无后效性的一位一位往后推,我们需要记录当前推到 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=fi−1,j,p,0+fi−1,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=fi−1,j−1,p,1(连在第p个子串后边)+fi−1,j−1,p−1,0+fi−1,j−1,p−1,1
因为要连在第
p
p
p 个子串后边,所以一定由
i
−
1
i-1
i−1 选了的转移过来
当
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=fi−1,j,p,0+fi−1,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(n⋅mk),但是时间不像空间,这个复杂度是可以接受的。于是,完整算法就结束了。
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);
}