首先,我们看 1 0 9 + 7 10^9+7 109+7这个数,它已经十分接近 i n t int int的最大数,所以要知道应该用 l o n g long long l o n g long long做。
接下来,我们来模拟一下样例1的第一组数据:
10-01-
遇到’-'那么删掉开头的
001-
遇到’-',再删掉开头的
01,这是第一种
那第二种方法就不再赘述了
我们再来看一组数据吧
S:111–00
T:100
首先看第一个’-',可以删结尾,也可以删开头,有两种可能
在来看第二个’-',与第一个一样,也有两种可能。
所以,一共有 2 ∗ 2 = 4 2*2=4 2∗2=4种可能
也就是说,多加一个’-',方案数就乘2(可能包含不能让Kri开心的)
我们设’-'的数量为t
所以方案数就是 2 t 2^t 2t个
那么我们看数据,一共有400个字符,所以可能会有上百个’-'那就是 2 100 2^{100} 2100种方案,枚举出来所有可能就只能拿部分分。所以我们要用DP计数了。
我们再定义一个R串
R: k k k j j j l l l
k k k:开头要删除的字符个数
j j j:已经与部分T串匹配好的字符个数
l l l:结尾要删除的字符个数
那我们假设一下 l l l后面还有一个 j j j
R : k k k j j j l l l j j j
那我们由于 j j j是匹配过的,就不能动,那怎么删 l l l,很明显,不能删,所以不能再 l l l后面添加东西。
好了,根据我们之前的归纳,我们知道有两个’-'有四种解法。
分别是:
尾 尾
头 头
头 尾
尾 头
那么我们把这四种情况带入到R串看看是什么情况
-
R : k k k j j j l l l
1 11
-
R : k k k j j j l l l
11 1
-
R : k k k j j j l l l
1 1 1
-
R : k k k j j j l l l
1 1 1
接下来我们来定义f数组
f[i][j][k][l]:判断到第i个字符(没用上),已经匹配了j个字符,开头要删除k个字符,末尾要删除l个字符。
初始状态:f[1][0][0][0]
- f[2][1][0][0]->f[3][1][0][1]->f[4][1][0][2]
- f[2][0][1][0]->f[3][0][2][0]->f[4][1][2][0]
- f[2][0][1][0]->f[3][1][1][0]->f[4][1][1][1]
- f[2][0][1][0]->f[3][1][1][0]->f[4][1][1][1]
第四组与第三组一样
f[i][j][k][l]->f[i+1][j+1][k][l] 匹配
f[i][j][k][l]->f[i+1][j][k][l+1] 没有匹配,放入 l l l区
f[i][j][k][l]->f[i+1][j][k+1][l] 没有匹配,放入
k
k
k区这个东西我们可以求出来,是
2
t
2^t
2t
f[i][j][k][l]->f[i+1][j][k-1][l] 遇到减号,删左边
f[i][j][k][k]->f[i+1][j][k][l-1] 遇到减号,删右边
那么如果我们用四维数组,对上数据,得算 40 0 4 400^4 4004
但是我们的 l l l却是可以求出来的:是 i − 1 − 2 t − j − k i-1-2t-j-k i−1−2t−j−k
那么就可以降一维了
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 405, MOD = 1e9+7;
ll f[N][N][N],sum[N];
char s[N],t[N];
ll q_pow(ll a,ll b)//快速幂
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%MOD;
a=a*a%MOD;
b/=2;
}
return ans;
}
int main()
{
int q,n,m,i,j,k;
cin >> q;
while(q--)
{
scanf("%d%d%s%s", &n, &m, s+1, t);
for (int i = 1; i <= n; i ++ )
{
if(s[i]=='-')
sum[i]=sum[i-1]+1;
else
sum[i]=sum[i-1];
}
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i ++ )//判断s串的第i个字符
{
for(int j = 0; j <= min(i,m);j++)//T串的前j个字符已经匹配
{
for(k = 0; k < i; k ++ )//R串开头的k个字符需删除
{
int l=i-1-k-j-sum[i-1]*2;//R串末尾的l个字符需要删除
if(l<0) break;
if(j == 0 && l == 0) //R串只有前k个要删除的字符,此时方法数=2^(sum[i-1]),有一个'-'就有两种方法
f[i][0][k]=q_pow(2,sum[i-1]);//第一次f[1][0][0]=1
if(s[i]!='-')
{
f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%MOD;//不匹配
if(j!=m && s[i]==t[j] && l==0)//匹配
f[i+1][j+1][k]=(f[i+1][j+1][k]+f[i][j][k])%MOD;
}
else//是'-'要么删左边,要么删右边
{
if(k)//删左边,左边是连续的
f[i+1][j][k-1]=(f[i+1][j][k-1]+f[i][j][k])%MOD;
if(l)//删右边,右边是连续的
f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%MOD;
}
}
}
}
printf("%lld\n",f[n+1][m][0]);
}
return 0;
}