G-subsequence 1 (dp+组合数学)
题意
给出两个字符串a和b且len(b)≤len(a),len(b)\leq len(a) ,len(b)≤len(a),问aaa中有多少大于bbb的子序列
题解
答案分为两部分
n=len(a),m=len(b)n=len(a),m=len(b)n=len(a),m=len(b)
1、任何非零开头长度大于len(b)len(b)len(b)的子序列都符合条件,这部分可以用组合数学来完成
ans=∑i=1len(a)∑j=mn−i(n−ij)(a[i]≠′0′)ans=\sum_{i=1}^{len(a)}\sum_{j=m}^{n-i}{n-i\choose j}(a[i]\neq {'0'})ans=∑i=1len(a)∑j=mn−i(jn−i)(a[i]̸=′0′)
稍微解释一下,为什么是(n−ij){n-i\choose j}(jn−i)
因为a[i]a[i]a[i]为选择的首位字符,这个子序列的长度要大于m,所以要在剩下的n−in-in−i个字符中至少选j,(m≤j≤n−i)j,(m\leq j\leq n-i)j,(m≤j≤n−i)个字符,是组合数。
2、第二部分是长度为len(b)len(b)len(b)且大于bbb的子序列数量
设dp[i][j]dp[i][j]dp[i][j]是后iii个字符组成的后缀中,长度为jjj符合条件子序列的个数
状态转移方程如下
dp[i][j]={dp[i−1][j]a[i]<b[j]dp[i−1][j]+dp[i−1][j−1]a[i]=b[j]dp[i−1][j]+(i−1j−1)a[i]>b[j]dp[i][j]=
\begin{cases}
dp[i-1][j]&a[i]<b[j]\\
dp[i-1][j]+dp[i-1][j-1]&a[i]=b[j]\\
dp[i-1][j]+{i-1\choose j-1}&a[i]>b[j]
\end{cases}dp[i][j]=⎩⎪⎨⎪⎧dp[i−1][j]dp[i−1][j]+dp[i−1][j−1]dp[i−1][j]+(j−1i−1)a[i]<b[j]a[i]=b[j]a[i]>b[j]
dp[n][m]dp[n][m]dp[n][m]即为这部分的和
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mkp(a,b) make_pair(a,b)
#define F first
#define ep(a) emplace_back(a)
#define S second
#define pii pair<int,int>
#define mem0(a) memset(a,0,sizeof(a))
#define mem(a,b) memset(a,b,sizeof(a))
#define MID(l,r) (l+((r-l)>>1))
#define ll(o) (o<<1)
#define rr(o) (o<<1|1)
const int INF = 0x3f3f3f3f ;
const int maxn = 3000+100;
const double pi = acos(-1.0);
int T,n,m;
typedef long long LL;const LL mod=998244353;
LL comb[maxn][(maxn>>1)+1];
inline LL C(int n,int k){
if(k>n)return 0;
k = min(k,n-k);
return comb[n][k];
}
void solve(){
for(int i = 0;i<maxn;i++){
comb[i][0] = 1;
for(int j = 1;j<=(i>>1);j++){//j<=i/2
comb[i][j] = (C(i-1,j-1) + C(i-1,j))%mod;
}
}
}
char a[maxn],b[maxn];
LL dp[maxn][maxn];
int main(){
memset(comb,0,sizeof(comb));
solve();
cin>>T;
while(T--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++)dp[i][j]=0;
}
scanf("%s%s",a+1,b+1);
LL ans=0;
for(int i=1;i<=n;i++){
if(a[i]=='0')continue;
for(int j=m;j<=n-i;j++){
ans=(ans+C(n-i,j))%mod;
}
}
reverse(a+1,a+1+n);
reverse(b+1,b+1+m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]<b[j])dp[i][j]=dp[i-1][j];
else if(a[i]==b[j])dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;
else dp[i][j]=(dp[i-1][j]+C(i-1,j-1))%mod;
}
}
ans=(ans+dp[n][m])%mod;
cout<<ans<<endl;
}
// system("pause");
return 0;
}