统计任意字符串中回文字符串的个数

题目来源:

庞果英雄会

 

题目详情:

回文字符串是指从左到右和从右到左相同的字符串,现给定一个仅由小写字母组成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。 输入:非空仅由小写字母组成的字符串,长度不超过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;

新浪微博 赶紧做毕设。

欢迎关注,那样可以多多交流嘛!

 

 

 

 

 

    原文作者:爱上健身的菇凉
    原文地址: https://blog.csdn.net/XIAXIA__/article/details/10033251
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞