xiaoxin juju needs help HDU - 5651 (有重复的全排列,逆元)

 xiaoxin juju needs help

HDU - 5651
As we all known, xiaoxin is a brilliant coder. He knew **palindromic** strings when he was only a six grade student at elementry school.
This summer he was working at Tencent as an intern. One day his leader came to ask xiaoxin for help. His leader gave him a string and he wanted xiaoxin to generate palindromic strings for him. Once xiaoxin generates a different palindromic string, his leader will give him a watermelon candy. The problem is how many candies xiaoxin's leader needs to buy?

Input This problem has multi test cases. First line contains a single integerT(T20) which represents the number of test cases.
For each test case, there is a single line containing a string S(1length(S)1,000)
.
Output For each test case, print an integer which is the number of watermelon candies xiaoxin's leader needs to buy after mod1,000,000,007 .
Sample Input
3
aa
aabb
a
Sample Output
1
2
1


题意:给你一个字符串,求打乱字符后,有多少种回文串

 

知识点:

    n个元素,其中a1,a2,····,an互不相同,进行全排列,可得n!个不同的排列。

    若其中某一元素ai重复了ni次,全排列出来必有重复元素,其中真正不同的排列数应为 clip_image002[4],即其重复度为ni!

    同理a1重复了n1次,a2重复了n2次,····,ak重复了nk次,n1+n2+····+nk=n。

    对于这样的n个元素进行全排列,可得不同排列的个数实际上是 clip_image001

   

题解:

字符串长度为len,若每个元素的重复次数ni为奇数的个数count>1,则无法形成回文串。

当能构成回文串时,我们只需考虑这个回文串左半部分的情况,所以这个问题也就变成了求一半字符串的有重复的全排列。

因为涉及到除法取模的问题,所以用到逆元。

逆元可以用扩展欧几里得,或费马小定理。/*6.逆元*/

为什么要求逆元:因为

费马小定理:取模运算的等阶式不存在除号的运算只能用乘号,所以要求逆元而不是直接除再取模


code:

#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;
}


你可能感兴趣的:(组合数学,数论)