[计蒜之道复赛 2018] 贝壳找房计数比赛

Description

贝壳找房举办了一场计数比赛,比赛题目如下。

给一个字符串 (s) 和字符串 (t),求出 (s) 的所有去重全排列中 (t) 出现的次数。比如aab的去重全排列为aabababaa。注意aaaa算出现两次aaa

你的老大希望你帮他写一个程序作弊。

Input

第一行一个整数 (T),表示数据组数。

每组数据中,第一行一个字符串 (s),第二行一个字符串 (t)

数据保证 (1le T le 100), (1le |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
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/YoungNeal/p/9247080.html