Double Strings
题意:
给你s和t两个字符串,在其中选出两个等长的子序列(可以不连续)a,b,满足a的字典序严格小于b的字典序,问方案数,答案mod(1e9+7)
题解:
好的方案的构成是一段相同的前缀+一个不同的字符(a比b小)+长度相同的任意后缀,按照这样构造一定是合法且包含所有情况
我们用dp[i][j]表示只考虑A的前i个字符和B的前j个字符时的相同的子序列的个数(有点类似于最长公共子序列,不过我们这里记录的是方案数)
转移方程可得:dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1](加上之前的情况减去重复的)
当a[i]=b[j]时,dp[i][j]=dp[i-1][j-1]+1
现在是如何求长度相同的任意后缀,我们假设A中剩余长度是x,B中剩余长度位y,设x<=y,那么求长度相同的任意后缀,就是A和B中分别取i个(i最大到x),有:∑i=0xCxiCyi=∑i=0xCxx−iCyi=Cx+yx\sum_{i=0}^{x}C_{x}^{i}C_{y}^{i}=\sum_{i=0}^{x}C_{x}^{x-i}C_{y}^{i}=C_{x+y}^{x}∑i=0xCxiCyi=∑i=0xCxx−iCyi=Cx+yx
每步推导过程都是用的常规组合数公式变换
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){ll s=0,w=1ll;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
void rd_txt(){#ifdef ONLINE_JUDGE#elsefreopen("in.txt","r",stdin);#endif
}
const int maxn=1e6+9;
const int mod=1e9+7;
ll fac[maxn];
ll inv[maxn];
ll dp[6000][6000];
char s[maxn],t[maxn];
int C(int n,int m) {if(n<m) {return 0;}return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll poww(ll a,ll b){ll ans=1;while(b){if(b&1){ans=ans*a%mod;}a=a*a%mod;b>>=1;}return ans;
}
void init() {fac[0]=1;int N=1e6;for(int i=1;i<=N;i++) {fac[i]=1LL*fac[i-1]*i%mod;}inv[N-1]=poww(fac[N-1],mod-2);for(int i=N-2;i>=0;i--) {inv[i]=1LL*inv[i+1]*(i+1)%mod;}
}
int main()
{init();scanf("%s%s",s+1,t+1);int n=strlen(s+1),m=strlen(t+1);for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) {dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])%mod;if(s[i]==t[j]) {dp[i][j]=(dp[i][j]+dp[i-1][j-1]+1)%mod;}dp[i][j]=(dp[i][j]+mod)%mod;}}ll ans=0;for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) {if(s[i]<t[j]) {ans=(ans+1LL*(dp[i-1][j-1]+1)*C(n-i+m-j,n-i))%mod;}}}cout<<ans<<endl;return 0;
}