3
aa
aabb
a
Sample Output
1
2
1
题意:给你一个字符串,求打乱字符后,有多少种回文串
知识点:
n个元素,其中a1,a2,····,an互不相同,进行全排列,可得n!个不同的排列。
若其中某一元素ai重复了ni次,全排列出来必有重复元素,其中真正不同的排列数应为 ,即其重复度为ni!
同理a1重复了n1次,a2重复了n2次,····,ak重复了nk次,n1+n2+····+nk=n。
题解:
字符串长度为len,若每个元素的重复次数ni为奇数的个数count>1,则无法形成回文串。
当能构成回文串时,我们只需考虑这个回文串左半部分的情况,所以这个问题也就变成了求一半字符串的有重复的全排列。
因为涉及到除法取模的问题,所以用到逆元。
逆元可以用扩展欧几里得,或费马小定理。/*6.逆元*/
为什么要求逆元:因为
费马小定理:取模运算的等阶式不存在除号的运算只能用乘号,所以要求逆元而不是直接除再取模
#include
#include
#include
#include
using namespace std;
#define MOD 1000000007
typedef long long ll;
int num[30];
ll q_pow(ll a,ll b){
ll ans = 1;
ll x = a % MOD;
while(b){
if(b&1)
ans = ans * x % MOD;
b >>= 1;
x = x * x % MOD;
}
return ans;//快速幂取模运算
}
ll getInverse(ll n){
ll i;
ll x = 1;
for(i = 1; i <= n; i++){
x = x * i % MOD;//先求出这个重复度,即出现次数的阶乘
}
return q_pow(x,MOD-2);//利用费马小定理求逆元
}
int main(){
int t;
scanf("%d",&t);
while(t--){
string s;
cin >> s;
memset(num,0,sizeof(num));
ll i;
for(i = 0; i < s.length(); i++){
num[s[i]-'a']++;//记录下每个字母出现的次数
}
int odd = 0;
int flag = 0;
for(i = 0; i < 27; i++){
if(num[i]&1)odd++;
if(odd > 1){
flag = 1;//统计出现奇数次的字母个数,超过1个就不能构成回文,直接进行下一次输入
printf("0\n");
break;
}
}
if(flag)continue;
ll ans = 1;
for(i = 1; i <= s.length()/2; i++){
ans = ans * i % MOD;//先求所有的阶乘,是长度的一半,因为只看一半就可以了
}
for(i = 0; i < 27; i++){
if(num[i] > 0){
ll x = getInverse((ll)num[i]/2);//求逆元,注意这里传进去的参数是个数的一半,因为只看一半就可以了
ans = ans * x % MOD;//依次除去重复度
}
}
printf("%lld\n",ans);
}
return 0;
}