大致思路是数位dp里当前位limit的那一套思想,从前往后递推计算,卡常数比较恶心
/* i:1->n 枚举第i位[a,z] 第i位<s[i]:那么后面就是个全排列,维护一个当前已经用掉的字符桶use,算出贡献直接加到答案里 算贡献:(n-i)!/(cnta-usea)!...(cntz-usez)! 如果暴力求复杂度得1e6*26*26 所以我们预处理好一个tmp=(n-i)!/(cnta-usea)!...(cntz-usez)! 当第i位选择ch时,将(cnt[ch]-use[ch])!这个底数换成(cnt[ch]-use[ch]-1)!即可 第i为==s[i]:后面的排列个数需要通过i+1位来确定,所以将第i位确定为s[i],更新use即可 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define mod 1000000007 #define N 1000005 char a[N],b[N]; ll n,cnt[N],use[N],F[N],invf[N],inv[N]; inline ll mul(ll a,ll b){ return a*b%mod; } ll solve(char *s){ ll res=0; memset(use,0,sizeof use); for(int i=1;i<=n;i++){//前i-1位都和s相同,第i位遍历[a,z] int equal=0; ll tmp=F[n-i],tmpp; for(int k=0;k<26;k++)//先预处理一个值,防止后面累乘 if(cnt[k]>use[k]) tmp=mul(tmp,invf[cnt[k]-use[k]]); tmpp=tmp; for(int j=0;j<26;j++){//第i位放j if(cnt[j]-use[j]==0)continue;//已经用完了 if(j<s[i]-'a'){ tmp=mul(tmp,F[cnt[j]-use[j]]);//消去原来j底数的影响 tmp=mul(tmp,invf[cnt[j]-use[j]-1]);//增加j-1作为底数 res=(res+tmp)%mod; tmp=tmpp;//恢复到原来的tmp } else if(j==s[i]-'a'){ use[j]++; equal=1;break; } } if(!equal)break; } return res; } int main(){ scanf("%s%s",a+1,b+1); //for(int i=1;i<=1000000;i++)a[i]='z',b[i]='z'; n=strlen(a+1); /*if(n>=10000){ cout<<n;return 0; }*/ F[0]=1;invf[0]=1;inv[1]=1; for(int i=2;i<=1000000;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<=1000000;i++)F[i]=F[i-1]*i%mod; for(int i=1;i<=1000000;i++)invf[i]=invf[i-1]*inv[i]%mod; for(int i=1;i<=n;i++)cnt[a[i]-'a']++; cout<<(solve(b)-solve(a)-1+mod)%mod<<' '; }