题目:
回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。
输入:非空仅由小写字母组成的字符串,长度不超过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);
}
最后感谢庞果网的各位网友的激情讨论