题目来源:
庞果英雄会
题目详情:
回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。 输入:非空仅由小写字母组成的字符串,长度不超过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) ;
题目分析:
1、统计输入字符串中每个字符出现的次数;
2、由于回文字符串左右对称,所以我只需要考虑以中心轴对称的一半就可以了。因此,可以将1中统计出的次数除以二;
3、这个问题就转换成了数学中的排列组合。即aaabbbbcccddd...一共有几种排列组合方法的问题。输入字符串长度除以二即为空位个
假设为M。根据数学知识可得回文字符串的个数为M!/(M1!*M2!*M3!.......*Mn!),其中M1....Mn为每个字符重复出现的次数。
题目解答:
经过上述的分析,心想这还是挺简单的嘛,一看十分之一的正确率,心里还是挺得意洋洋的。也找了几个简单的例子来测,也都符合自己的预期,便提交了答案。一看自己的答题结果,血淋淋的测试未通过几个字眼摆在了我的眼前。下面将第一版的代码贴出来。
unsigned long long int jieceng(int n) { unsigned long long int result=1; int i; for (i = 1; i <= n; i++) { result*=i; } return result; } int palindrome(const char *s) { int len = strlen(s); int num[26]={0}; int flag_len = len % 2; int flag_bit; int num_bit[26]={0}; int i; int block = len/2; unsigned long long int result; int flag=0; if (len > 100) { printf("Strings too long"); return 0; } for (i = 0; i < len; i++) { num[*(s+i)-'a']++; //统计出每个字母出现的个数 } for (i = 0; i < 26; i++) { flag_bit = num[i] % 2; if (flag_bit == 1) { flag++; } if ((flag_len == 0 && flag_bit == 1) || (flag_len == 1 && flag>1)) { return 0; } num_bit[i] = num[i]/2; } result=jieceng(block); for (i = 0; i < 26; i++) { if (num_bit[i]==0) { continue; } result=result/jieceng(num_bit[i]); } result=result%1000000007; printf("Result is %d \n",result); return result; } int _tmain(int argc, _TCHAR* argv[]) { char str[101] = "hqaymehhrsfuqrpahrimsxftuxqrpsejouuehaqtsryxjhearxmogmi"; palindrome((const char *)(&str)); return 0; }
吃了闭门羹之后,很是郁闷,这不应该有什么问题啊。可能是自己缺少编程经验吧,调试了好一会儿才发现时求大数阶乘的时候会有溢出的问题。于是便寻找解决办法。也想了挺久的,其实没必要那么傻啊,没必要按照这个公式M!/(M1!*M2!*M3!.......*Mn!)先把每个数的阶乘算出来,可以拆成(1*2*3*...*N...M)/(M1!*M2!*M3!.......*Mn!),在遇到能约分的情况就约分,当分母全部被约掉之后,再判断这个时候的乘积是否大于1000000007,如果大于就取余。这样就不会有溢出的问题啦。所以有了第二版的代码。
// ConsoleApplication2.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "stdlib.h" #include "string.h" #include "stdio.h" int jieceng(int n) { int result=1; for (int i = 1; i <= n; i++) { result=result*i; } return result; } int calc_result(int block, int *p) { //int *pointer=(int *)malloc(block*sizeof(int)); unsigned __int64 result_mid=1; unsigned __int64 result_mid_big=1; for (int i = 1; i <= block; i++) { int sum=0; result_mid_big=result_mid_big*i; for (int j = 0; j < 26; j++) { sum+=*(p+j); if (*(p+j)==0) { continue; } result_mid=jieceng(*(p+j)); if (result_mid_big%result_mid==0) { result_mid_big=result_mid_big/result_mid; *(p+j)=0; } } if (sum==0) { if (result_mid_big>1000000007) { result_mid_big=result_mid_big%1000000007; } } } return (int)result_mid_big; } int palindrome(const char *s) { int len = strlen(s); if (len > 100) { printf("Strings too long"); return 0; } int num[26]={0}; int flag_len = len % 2; int flag_bit,flag=0; int num_bit[26]={0}; for (int i = 0; i < len; i++) { num[*(s+i)-'a']++; //统计出每个字母出现的个数 } for (int i = 0; i < 26; i++) { flag_bit = num[i] % 2; if (flag_bit==1) { flag++; } if ((flag_len == 0 && flag_bit == 1) || (flag_len == 1 && flag > 1)) //字符串长度为偶数,但是有出现基数词的字符,则不可能是回文字符 { return 0; } num_bit[i] = num[i]/2; } int block = len/2; int result=calc_result(block,(int *)&num_bit); printf("Result is %d \n",result); return result; } int _tmain() { char str[101] = "vlvdutxonuavcuwpkygenywmwofdubhhiqswkwgfbbbyjvlleqrrbmbkcfkqrpnyulzrkiwsbkauygajaqdybmxmqqcfygcdnty"; palindrome((const char *)(&str)); return 0; }
再一想,其实这样也还是有问题,只要用到了求阶乘的函数,就还是会有问题。在本题中,最多为100个字符,考虑一半,最多只有50个字符,每个字符出现的次数正常的话,也不会导致溢出。但是总是会有意外的啦,比如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan这样呢?上面的代码就死翘翘啦。所以我决定对代码进一步改进,摈弃阶乘这个函数。(1*2*3*....M)/(M1!*M2!*M3!.......*Mn!)在这个公式中,在分母中为了不进行阶乘运算,所以我决定统计出2、3.....max(M1,M2....Mn)出现的次数,分母一边进行乘法运算的时候,一边看能否整除2、3.....max(M1,M2....Mn),如果能整除,出现次数就减1,直到所有出现次数都为0时,判断此时的乘积是否大于1000000007,若大于则取余之后再进行乘法。依次类推。这形成下面第三版代码。
// ConsoleApplication2.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "stdlib.h" #include "string.h" #include "stdio.h" #define max(a,b) a>b?a:b int calc_result(int block, int *p,int len) { unsigned __int64 result_mid=1; unsigned __int64 result_mid_big=1; for (int i = 1; i <= block; i++) { int sum=0; result_mid_big=result_mid_big*i; for (int j = 2; j <= len; j++) { sum+=*(p+j); if (*(p+j)==0) { continue; } if (result_mid_big%j==0) { result_mid_big=result_mid_big/j; (*(p+j))--; } } if (sum==0) { if (result_mid_big>1000000007) { result_mid_big=result_mid_big%1000000007; } } } return (int)result_mid_big; } int palindrome(const char *s) { int len = strlen(s); if (len > 100) { printf("Strings too long"); return 0; } int num[26]={0}; int flag_len = len % 2; int flag_bit,flag=0; int num_bit[26]={0}; int MAX=0; for (int i = 0; i < len; i++) { num[*(s+i)-'a']++; //统计出每个字母出现的个数 } for (int i = 0; i < 26; i++) { flag_bit = num[i] % 2; if (flag_bit==1) { flag++; } if ((flag_len == 0 && flag_bit == 1) || (flag_len == 1 && flag > 1)) //字符串长度为偶数,但是有出现基数词的字符,则不可能是回文字符 { return 0; } num_bit[i] = num[i]/2; MAX = max(MAX,num_bit[i]); } int *p_num = new int[MAX+1](); for (int i = 0; i < 26; i++) { if (num_bit[i]<=1) { continue; } for (int j = 2; j <= num_bit[i]; j++) { (*(p_num+j))++; } } int block = len/2; int result=calc_result(block,p_num,MAX); printf("Result is %d \n",result); delete[] p_num; return result; } int _tmain() { char str[101] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan"; palindrome((const char *)(&str)); return 0; }
啊,总算可以歇口气了。我的代码还存在很多问题,希望路过的大侠们多给点建议,高手勿喷。
微信账号 forever19910521;
新浪微博 赶紧做毕设。
欢迎关注,那样可以多多交流嘛!