【codechef】交换字符串S的两个位置上的字符,求有多少对AB不相似

有一种操作,是交换字符串的两个位置上的字符(位置可以一样)。对于两个字符串A、B,如果分别对它们做这个操作后得出一样字符串C,那么称AB相似。

现在给出一个字符串S,A和B分别都是它的全排列里的一种。现在求有多少对A、B满足AB不相似。

//相似:A变化成C,B也变化成C,那么AB相似。那么不妨设B不动,A变化两次是否能到B。 
//假设B是随便取的,A是顺着B取的,那么B在取每一种排列时都有某几种A的排列要避开它的。而这些避开它的A也不用胡乱取,它只会和当前这一种B的排列很像。
//下面做法还有一个妙处是:它是按26个字母来分的。这样就不用考虑长度了 
#include <bits/stdc++.h>
using namespace std;
const int MOD=1000000007;
int fact[100001];
char S[100001];
int cnt[26];
int powmod(int a, int b){
    int ret=1;
    for(; b>0; b/=2){
        if(b&1)
            ret=1LL*ret*a%MOD;
        a=1LL*a*a%MOD;
    }
    return ret;
} 
int inv(int x){
    return powmod(x, MOD-2);
}
int C2(int x){
    return 1LL*x*(x-1)/2%MOD;
} 
void _main(){
    memset(cnt, 0, sizeof cnt);
    scanf("%s", S);
    int N=strlen(S);
    for(int i=0; i<N; i++)
        cnt[S[i]-'a']++;
    int total=fact[N];
    for(int i=0; i<26; i++)
        total=1LL*total*inv(fact[cnt[i]])%MOD; //字符串排列总数
    int sub=1;   //1是指AB完全一样的情况 
    for(int i=0; i<26; i++)
        for(int j=i+1; j<26; j++)
            sub=(sub+1LL*cnt[i]*cnt[j])%MOD; //和B比起来只有某两位不一样的互换了 
     for(int i=0; i<26; i++)
        for(int j=i+1; j<26; j++)
            for(int k=j+1; k<26; k++)
                sub=(sub+2LL*cnt[i]*cnt[j]%MOD*cnt[k])%MOD; //和B比起来只有某三位不一样的互换了,可以ab,ac也可以ac,ab,所以*2 
	for(int i=0; i<26; i++)
        for(int j=i+1; j<26; j++)
            for(int k=j+1; k<26; k++)
                for(int l=k+1; l<26; l++)
                    sub=(sub+3LL*cnt[i]*cnt[j]%MOD*cnt[k]%MOD*cnt[l])%MOD; //和B比起来只有某四位不一样的互换了  
	for(int i=0; i<26; i++)
        for(int j=i+1; j<26; j++)
            sub=(sub+1LL*C2(cnt[i])*C2(cnt[j]))%MOD;  //取两对分别同样的字母,交叉互换(某四位不一样) 
    for(int i=0; i<26; i++)
        for(int j=0; j<26; j++) if(j!=i)
            for(int k=j+1; k<26; k++) if(k!=i)
                sub=(sub+2LL*C2(cnt[i])*cnt[j]%MOD*cnt[k])%MOD; //取【一对同样字母】和【其它两个不同的字母】互换位置,因为其它两个不同字母换到那一对的位置上有两种换法,所以*2(某四位不一样)  
    int ans=1LL*(total-sub+MOD)*total%MOD; //假设B是随便取的,A是顺着B取的 
    printf("%d\n", ans);
}
 
int main(){
    fact[0]=1;
    for(int i=1; i<=100000; i++)
        fact[i]=1LL*fact[i-1]*i%MOD;
    int TEST;
    scanf("%d", &TEST);
    while(TEST--)
        _main();
    return 0;
}


你可能感兴趣的:(【codechef】交换字符串S的两个位置上的字符,求有多少对AB不相似)