题目来源:
庞果英雄会
题目详情:
回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。 输入:非空仅由小写字母组成的字符串,长度不超过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;
新浪微博 赶紧做毕设。
欢迎关注,那样可以多多交流嘛!