题目:
回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。
输入:非空仅由小写字母组成的字符串,长度不超过100;
输出:能组成的所有回文串的个数(因为结果可能非常大,输出对1000000007取余数的结果)。
例如:输入"aabb" 输出为2(因为“aabb”对应的所有回文字符串有2个:abba和baab)
函数头部
c:
int palindrome(const char *s);
c++
int palindrome(const string &s);
java
public static int palindrome(String s) ;
分析:
该题目意图是给定一组字符,让你调整顺序构成回文字符串,统计回文字符串的数量。
观察回文字符串构成,可以确定:
a. 每个字符数量为偶数,可以构造出回文字符串,此时回文字符串的长度为偶数。比如题目给出的例子;
b. 仅有一个数量为奇数的字符,可以构造出回文字符串,此时回文字符串的长度为奇数。比如aab,可以构造出aba;
c. 有多个数量为奇数的字符时,不能构成回文字符串,比如,ab就无法构成回文字符串。
长度为偶数的回文字符串(长度为奇数时也一样分析,只要将奇数的那个字母去掉,因为此字母只能在对称点上):
将长度为偶数的回文字符串从中间切开,则每一侧的字符串长度为原来的一半,而且每个字符的数量也为原来的一半。比如aabb可以构成回文字符串abba,切开之后,每一侧为ab,含有原来一半的字符。
假设调整左半侧字符数按需,右侧也相应改变,那么将半侧的字符串求出全排列,则既可以得出所有回文字符串的数量。为了好理解,我再举个例子:
原始串:aaaabbcc,可以看出每个字符为偶数个,用|表示分割线,则所有的回文字符串为:
aabc|cbaa abac|caba abca|acba
aacb|bcaa acab|baca acba|abca
baac|caab baca|acab
bcaa|aacb
cbaa|aabc caba|abac
caab|baac
一共有12种,为(4!/2!),即半侧字符串的全排列。
由上可知,问题转化为:
1. {Ai | 0<i<=n,n为字符种类数目};
2. Ai中奇数超过1个时返回0;
3. sum += Ai/2, M *= Ai!;
4. N = sum!;
5. N/M
在处理第3,4步时,采用分解质因数方法求中间结果,否则会出现溢出。
#include <map> #include <iostream> using namespace std; const int MOD = 1000000007; //通过分解质因数,统计每个质数的个数并保存在map中,根据分子还是分母情况在map中做加或减操作 void primeAddStep(int n,map<int,int>& prime,int step){ while(n){ int tmp = n; for (int i=2;i<=tmp;i++){ while((tmp>=i) && (0 == tmp%i)){ prime[i] += step; tmp=tmp/i; } } --n; } } int palindrome(const string &s) { //保存每个字符数目 map<char,int> charCount; //分解质因数用 map<int,int> primeCount; map<char,int>::iterator iter_charCount; map<int,int>::iterator iter_primeCount; int sum = 0; int flag = 0; __int64 res = 1; //统计每个字符的个数 for(string::size_type i=0;i<s.size();++i){ iter_charCount=charCount.find(s[i]); if(iter_charCount != charCount.end()) ++iter_charCount->second; else charCount.insert(pair<char,int>(s[i],1)); } for(iter_charCount=charCount.begin();iter_charCount!=charCount.end();++iter_charCount){ //判断字符数目是否为奇数,若奇数超过1个直接返回0 if(iter_charCount->second&0x01){ if(0==flag) ++flag; else return 0; } //将字符数目减半 iter_charCount->second >> 1; //累加减半后的字符数目 sum += iter_charCount->second; //采用分解质因数方法,计算减半后的字符数目的阶乘的积 primeAddStep(iter_charCount->second,primeCount,-1); } //采用分解质因数方法,计算累计和的阶乘/字符数目阶乘积 primeAddStep(sum,primeCount,1); //根据约分之后的结果,计算出最终数量 for(iter_primeCount=primeCount.begin();iter_primeCount!=primeCount.end();++iter_primeCount){ for(int i=0;i<iter_primeCount->second;++i){ res = (res*iter_primeCount->first)%MOD; } } return int(res); }