2021牛客多校5 D Double Strings
阿新 • • 發佈:2021-08-01
那麼這個題的題意非常的抽象,在做題之前我們不妨仔細地,認真地,多讀幾遍題,然後我們可以發現題意大概是 :
在 兩個字串中 ,分別選出長度相等地子序列,使得從第二個中選出地子序列的字典序大小大於第一個
那麼我們就可以將這兩個子序列分段,分為
“一段相同的字首 + 一個不同的字元 (第一個比第二個小) + 任意長度相同的字尾”
這樣就將問題分解了。
於是我們需要求的就是
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; }