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

Description

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

给一个字符串 \(s\) 和字符串 \(t\),求出 \(s\) 的所有去重全排列中 \(t\) 出现的次数。比如aab的去重全排列为aabababaa。注意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;
}
posted @ 2018-06-30 14:31  YoungNeal  阅读(231)  评论(0编辑  收藏  举报