给定两个相同长度的字符串S1和S2,求由S1重新排列产生S3,且字典序(S1<S3<=S2)的种类数
我的题解:O(NlgN)
分别统计字典序小于S2和小于S1的排列数,Ans=Ans2-Ans1-1(不能与S1相同)
有重复元素的排列数:sum表示元素总数,cnt[]表示元素个数
统计S1中每个字符串出现的次数记为cnt[],树状数组动态维护26个字母出现次数的前缀和,
对于1~L长度上的每一个字符,(1).若存在比它小的字符则统计其贡献,(2).若存在与它相同的字符则把一个该字符放在当前位置并继续统计下一个位置
在(1)中统计时会有优化,不优化的复杂度为O(NlgN*26*26),方法是预先处理
因为cnt[]是动态变化的,所以 mul 也需要动态修改
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+5;
const long long MOD=1e9+7;
char s1[MAX],s2[MAX];
int L,c[30],cnt[30];
long long fact[MAX],inv[MAX],mul,ans1,ans2;
long long inv_(long long a,long long m)
{
if(a==1) return 1;
return inv_(m%a,m)*(m-m/a)%m;
}
void init()
{
fact[0]=1;inv[0]=1;
for(int i=1;i<MAX;i++)
{
fact[i]=fact[i-1]*i%MOD;
inv[i]=inv_(fact[i],MOD);
}
}
void add(int x,int v)
{
while(x<=27) c[x]+=v,x+=x&(-x);
}
int get(int r)
{
int sum=0;
while(r) sum+=c[r],r-=r&(-r);
return sum;
}
void dfs(char *s,int u,long long &ans)
{
int x=s[u]-'a'+1;
if(get(x-1))
{
for(int i=1;i<x;++i) if(cnt[i])
ans=(ans+fact[L-u]*mul%MOD*cnt[i]%MOD)%MOD;
}
if(cnt[x]&&u<L)
{
add(x,-1);
long long t=mul;
mul=mul*cnt[x]%MOD;
--cnt[x];
dfs(s,u+1,ans);
++cnt[x];
mul=t;
add(x,1);
}
}
int main()
{
init();
while(~scanf("%s%s",s1+1,s2+1))
{
ans1=ans2=0;L=strlen(s1+1);memset(c,0,sizeof(c));memset(cnt,0,sizeof(cnt));
for(int i=1;i<=L;++i) {++cnt[s1[i]-'a'+1];add(s1[i]-'a'+1,1);}
mul=1;for(int i=1;i<=26;++i) mul=inv[cnt[i]]*mul%MOD;
dfs(s1,1,ans1);dfs(s2,1,ans2);
printf("%lld\n",(ans2-ans1-1+MOD)%MOD);
}
return 0;
}