题意:
给一字符串,问用这个字符串里面的字符可以拼成多少个不同的回文串
解析:
把回文串分成左右两边,即字母x出现次数除2,求排列C
假设左半边有x个字母a,y个字母b,z个字母c,C==(x+y+z)! / x! / y! / z!
普通的做法有个bug,(x+y+z)!当然可以做,但是%mod后再除x!呢?举个例子,10000/100可以除,但是10000%107再/100就会造成精度上的错误。
用了逆元就完全不用担心了,我们先求出(x+y+z)!,再乘x!的逆元,y!的逆元,z!的逆元,就避免了这种错误。
代码:
#include<stdio.h>
#include<string>
#include<iostream>
#define D long long
const D mod=(D)(1e9+7);
using namespace std;
D swift(D a,D b){
D bas=a;D mul=1;
while(b){
if(b&1)mul=mul*bas%mod;
bas=bas*bas%mod;b>>=1;
}return mul;
}
D inv_fac[1007];
D fac[1007];
void init(){
fac[1]=1;
for(D i=2;i<=1000;i++)fac[i]=fac[i-1]*i%mod;//初始化阶乘
inv_fac[1000]=swift(fac[1000],mod-2);//费马小定理求 1000!的逆元
for(D i=999;i>=0;i--)
inv_fac[i]=(inv_fac[i+1]*(i+1))%mod; //预处理 i!的逆元
}
void process(){
string x;
cin>>x;
if(x.length()==1){printf("1\n");return ;}
int nu[27];for(int i=1;i<=26;i++)nu[i]=0;
for(int i=0;i<x.length();i++)nu[x[i]-'a'+1]++;
int tji=0;
for(int i=1;i<=26;i++)if(nu[i]%2==1)tji++;
if(tji>1){printf("0\n");return;}
//因为取一半,所以奇数的那个不用管
D ans=fac[x.length()/2];
for(int i=1;i<=26;i++){
if(!nu[i]||nu[i]==1)continue;
ans=ans*inv_fac[nu[i]/2]%mod;
}
printf("%lld\n",ans);
}
int main(){
init();
int t;
scanf("%d",&t);
while(t--){
process();
}
}
/*
3
*/