2021牛客多校5 D Double Strings
那么这个题的题意非常的抽象,在做题之前我们不妨仔细地,认真地,多读几遍题,然后我们可以发现题意大概是 :
在 两个字符串中 ,分别选出长度相等地子序列,使得从第二个中选出地子序列的字典序大小大于第一个
那么我们就可以将这两个子序列分段,分为
“一段相同的前缀 + 一个不同的字符 (第一个比第二个小) + 任意长度相同的后缀”
这样就将问题分解了。
于是我们需要求的就是
1: A,B两个字符串所具有的相同的前缀的个数
令\(dp[i][j]\)表示第一个字符串前i个位置和第二个字符串前j个位置所具有的相同的子序列,这里空字符串也算一个子序列,因此得到初始状态和转移条件:
dp[i][0]=dp[0][j]=1;
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
if a[i]==b[j] : dp[i][j]+=dp[i-1][j-1];
2:任意长度相同的后缀的个数
对于当前位置 i,j ,不妨令i > j,则选取任意后缀的方案数为
\(\sum_{x=0}^{n-i}C(n-i,x)*C(m-j,x)\)
\(=\sum_{x=0}^{n-i}C(n-i,n-i-x)*C(m-j,x)\)
\(=C(n-i + m-j,n-i)\)
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include <unordered_map>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rpe(i,a,b) for(int i=a;i>=b;--i)
#define pts putchar('\n')
#define ptc putchar(' ')
#define pb push_back
typedef long long ll;
typedef pair<int,int>P;
typedef unsigned long long ull;
const int inf=0x7f7f7f7f;
const ll linf=1e18;
const int maxn=5e3+9;
const int maxm=2e5+9;
const double PI=3.1415926;
const double eps=1e-5;
const ll mod=1e9+7;
const int base=131;
const int N=1e6;
char A[maxn],B[maxn];
int dp[maxn][maxn];
int n,m;
ll fac[maxn*2],inv[maxn*2];
ll qpow(ll a,ll b){
ll sum=1;
while(b){
if(b&1) sum=sum*a%mod;
a=a*a%mod;b>>=1;
}
return sum;;
}
void pre(){
fac[0]=1;
rep(i,1,10000) fac[i]=1LL*fac[i-1]*i%mod;
inv[10000]=qpow(fac[10000],mod-2);
rpe(i,9999,0) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
ll C(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main(){
scanf("%s %s",A+1,B+1);
n=strlen(A+1);m=strlen(B+1);
rep(i,0,n) dp[i][0]=1;
rep(i,0,m) dp[0][i]=1;
ll ans=0;pre();
rep(i,1,n){
rep(j,1,m){
dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])%mod;
if(A[i]==B[j]) (dp[i][j]+=dp[i-1][j-1])%=mod;
else if(A[i]<B[j]){
ans=(ans+ 1LL * dp[i-1][j-1] * C(n - i + m-j,n-i) %mod)%mod;;
}
}
}
printf("%lld",(ans+mod)%mod);
return 0;
}