庞果网---回文字符串

题目:

回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。

 

输入:非空仅由小写字母组成的字符串,长度不超过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);
}

最后感谢庞果网的各位网友的激情讨论


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值