[计蒜之道复赛 2018] 贝壳找房计数比赛
Description
贝壳找房举办了一场计数比赛,比赛题目如下。
给一个字符串 \(s\) 和字符串 \(t\),求出 \(s\) 的所有去重全排列中 \(t\) 出现的次数。比如aab
的去重全排列为aab
、aba
、baa
。注意aaaa
算出现两次aaa
。
你的老大希望你帮他写一个程序作弊。
Input
第一行一个整数 \(T\),表示数据组数。
每组数据中,第一行一个字符串 \(s\),第二行一个字符串 \(t\)。
数据保证 \(1\le T \le 100\), \(1\le |t| \le |s| \le 10^5\), \(t,s\) 只包含小写字母。
Output
输出一共 T 行,每行一个整数,表示所求答案对 \(10^9+7\) 取模的结果。
Solution
这个去重全排列有点吓人,实际上就是可重集合的排列数嘛!
那么我们可以枚举子串 \(t\) 在主串 \(s\) 中出现的位置,然后将 \(s\) 除了 \(t\) 剩下的部分重新排列更新答案即可。
注意 \(t\) 在 \(s\) 中出现的次数可以 \(\mathcal O(1)\) 求,也就是 \(n-m+1\) 次。
Code
#include<cstdio>
#include<cstring>
#define N 100005
#define int long long
const int mod=1e9+7;
int T;
int xx[N];
char a[N],b[N];
int fac[N],ifac[N];
int ksm(int a,int b){
int ans=1;
while(b){
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
signed main(){
fac[0]=1;ifac[0]=1;
for(int i=1;i<=N-5;i++)
fac[i]=fac[i-1]*i%mod,ifac[i]=ksm(fac[i],mod-2);
scanf("%lld",&T);
while(T--){
scanf("%s%s",a,b);
int n=strlen(a),m=strlen(b);
memset(xx,0,sizeof xx);
for(int i=0;i<n;i++)
xx[a[i]-'a']++;
for(int i=0;i<m;i++)
xx[b[i]-'a']--;
int ans=fac[n-m];
for(int i=0;i<26;i++)
ans=ans*ifac[xx[i]]%mod;
ans=ans*(n-m+1)%mod;
printf("%lld\n",ans);
}
return 0;
}
当你走进这欢乐场