[计蒜之道复赛 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;
}

猜你喜欢

转载自www.cnblogs.com/YoungNeal/p/9247080.html
今日推荐