HDU 5651 计算回文串个数问题(有重复的全排列、乘法逆元、费马小定理)

原题:

http://acm.hdu.edu.cn/showproblem.php?pid=5651

  1. 很容易看出来的是,如果一个字符串中,多于一个字母出现奇数次,则该字符串无法形成回文串,因为不能删减字母。
  2.  当能构成回文串时,我们只需考虑这个回文串左半部分的情况,所以这个问题也就变成了求一半字符串的有重复的全排列。
  3. 因为应用全排列公式中,会用大数除以大数再取余,除法不能简单的分子、分母取余再做除法,这时就要用到乘法逆元,同时用费马小定理求乘法逆元
  4. 相关公式:http://www.cnblogs.com/i2u9/p/full_seq.html
  5. 乘法逆元及其证明:http://www.cnblogs.com/tiankonguse/archive/2012/08/14/2638949.html
 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 1111
 4 #define mod 1000000007
 5 char s[maxn];
 6 int book[30];//记录每个字母出现次数 
 7 long long fact[maxn];//计算保存阶乘 
 8 //求快速幂 
 9 long long fpow(long long a,long long b){
10     long long res = 1;
11     while(b){
12         if(b&1)
13             res = res*a%mod;
14         b >>= 1;
15         a = a*a%mod;
16     }
17     return res;
18 }
19 int main(){
20     //计算阶乘,全排列用 
21     fact[0] = 1;
22     for(int i = 1;i<maxn;i++)
23         fact[i] = fact[i-1]*i%mod;
24     int t;
25     scanf("%d",&t);
26     while(t--){
27         memset(book,0,sizeof(book));
28         scanf(" %s",s);
29         for(int i = 0;s[i];i++){
30             book[s[i]-'a']++;
31         }
32         int cnt = 0;//计算出现奇数次字母的个数 
33         for(int i= 0;i<27;i++){
34             if(book[i]&1)
35                 cnt++;
36             book[i] /= 2; 
37         }
38         int len = strlen(s);
39         if(cnt >1){
40             puts("0");
41         }else{
42             long long ans = fact[len/2];
43             for(int i = 0;i<26;i++)
44                 if(fact[book[i]])
45                     ans = ans*fpow(fact[book[i]],mod-2)%mod;
46             printf("%I64d
",ans);
47         }
48     }
49     return 0;
50 }

y=n!x1!x2!x3!xk!y=n!x1!x2!x3!xk!y=n!x1!x2!x3!xk!y=n!x1!x2!x3!xk!

原文地址:https://www.cnblogs.com/zqy123/p/5331833.html