LHL算法入门经典 例题3-3最长回文子串

##3-3【字符串】最长回文子串

题目描述

输入一个字符串,求出其中最长的回文子串。子串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同。如abba和yyxyy。在判断回文时,应该忽略所有标点符号和空格,且忽略大小写,但输出应保持原样(在回文串的首部和尾部不要输出多余字符)。输入字符串长度不超过5000,且占据单独的一行。应该输出最长的回文串,如果有多个,输出起始位置最靠左的。

输入

一行字符串,字符串长度不超过5000。

输出

字符串中的最长回文子串。

样例输入

Confuciuss say:Madam,I'm Adam.

样例输出

Madam,I'm Adam

提示

样例说明:Madam,I’m Adam去掉空格、逗号、单引号、忽略大小写为MADAMIMADAM,是回文。

算法分析一:

首先解决“判断时忽略标点,输出进却要按原样”的问题? 可以用一个简单的方法:预处理。构造一个新字符串,不包含原来的标点符号,而且所有字符变成大写(顺便解决了大小写的问题)。用到的函数:

(1)isalpha©用来检查c是否为字母,如果是字母,则返回1;否则返回0。

(2)isdigit©用来检查c是否为数字(0~9),如果是数字,则返回1;否则返回0。

(3)toupper©用来将c字符转换为大写字母,返回c对应的大写字母。

(4)tolower©用来将c字符转换为小写字母,返回c对应的小写字母。

下面来枚举回文串的起点和终点,然后判断它是否真的是回文串。

int max=0;

for(i = 0; i < m; i++)

for(j = i; j < m; j++)

   if(s[i..j]是回文串 && j-i+1 > max) max = j-i+1;

“当前最大值”变量max,它保存的是目前为止发现的最长回文子串的长度。如果串s的第i个字符到第j个字符(记为s[i…j])是回文串,则检查长度j-i+1是否超过max。

判断s[i…j]是否为回文串的方法如下:

int ok = 1;

for(k = i; k <= j; k++)

if(s[k] != s[i+j-k])   ok = 0;

s[k]的“对称”位置是s[i+j-k],因为只要一次比较失败,就应把标记变量ok置为0。

#include<stdio.h>
#include<string.h>
#include<ctype.h>		//用到isalpha、touuper等工具
#define MAXN 5000+10
char buf[MAXN],s[MAXN];
int main()
{
	int n,m=0,max=0;
	int i,j,k;
	fgets(buf,sizeof(s),stdin);		//从标准输入流中读取sizeof(s)-1个字符并且把他们转储到buf中
	n=strlen(buf);					//获取buf的长度,包含文件结束符'\0'
	for(i=0;i<n;i++)  
							//构造一个新的字符串,把标点符号过滤掉,随便把小写字母变为大写
		if(isalpha(buf[i])) s[m++]=toupper(buf[i]);
	for(i=0;i<m;i++)		//其中m是新字符串s的长度
		for(j=i;j<m;j++)
		{
			int ok=1;
			for(k=i;k<=j;k++)	//判断s[i..j]是否为回文串
				if(s[k]!=s[i+j-k]) ok=0;	
			if(ok && j-i+1>max) max=j-i+1;	//保存当前发现的最长回文串
		}
		printf("max=%d\n",max);
		return 0;
}

最后的问题:原样输出。

由于在求max值时,不知道s[i]和s[j]在原串buf中的位置。因此,必须增加一个数组p,用p[i]保存s[i]在buf中的位置。在预处理得到,然后在更新max的同时把p[i]和p[j]保存到x和y,最后输出buf[x]到buf[y]中的所有字符。

不足:当输入字符串较长时,容易超时,因枚举回文起点和终点,循环过多。

#include<stdio.h>
#include<string.h>
#include<ctype.h>		//用到isalpha、touuper等工具
#define MAXN 5000+10
char buf[MAXN],s[MAXN];
int main()
{
	int n,j,k,i,l,f,max=0,ok;
	int m=0;
	fgets(buf,sizeof(buf),stdin);
	n=strlen(buf);
	for(i=0;i<n;i++)
	{
		if(isalpha(buf[i]))
		s[m++]=toupper(buf[i]);
	}
	
	for(i=0;i<m;i++)
	{
		for(j=i;j<m;j++)
		{
			ok=1;
			for(k=i;k<=j;k++)
			{
				if(s[k]!=s[i+j-k])
				ok=0;
			}
			if(ok&&j-i+1>max)
			max=j-i+1;
			
		}
		
	}
	printf("%d",max);
	
	return 0;
}
#include<stdio.h>
#include<string.h>
#include<ctype.h>		//用到isalpha、touuper等工具
#define MAXN 5000+10
char buf[MAXN],s[MAXN];
int p[MAXN];			//增设数组p,用于保存s[i]在buf中的位置
int main()
{
	int n,m=0,max=0,x,y;
	int i,j,k;
	fgets(buf,sizeof(s),stdin);	//从标准输入流中读取sizeof(s)-1个字符并且把他们转储到buf中
	n=strlen(buf);				//获取buf的长度,包含文件结束符'\0'
	for(i=0;i<n;i++)
		if(isalpha(buf[i]))		//构造一个新的字符串,把标点符号过滤掉,随便把小写字母变为大写
			{
				p[m]=i;			//保存s[m]在buf的位置
				s[m++]=toupper(buf[i]);	
		}
		for(i=0;i<m;i++)		//遍历字符串s,以i为"中间"位置,然后根据j的值不断向两边扩展
		{						//这个for循环遍历的子串长度为奇数
			for(j=0;i-j>=0 && i+j<m; j++)	//注意i和j的关系:i-j>=0表示i到j的距离不能上溢
											//i+j<m表示i再加j个位置没有超过字符串s的总长
			{
				if(s[i-j]!=s[i+j]) break;	//如果以i为中间点,左边i-j个点字符跟右边i+j个点不同,跳出循环
				if(j*2+1>max)				//因为子串的长度为奇数,所以子串的长度应该等于j*2+1(j为以i为中心,往两边的距离)
				{ max=j*2+1; x=p[i-j];y=p[i+j]; } //保存当前最长回文子串长度,记录子串范围
			}
			for(j=0;i-j>=0 && i+j+1<m; j++)//这个for循环遍历的子串长度为偶数,
				//中间点i取子串长度的中点,导致两边长度不均,右边的距离应该再加1
			{
				if(s[i-j]!=s[i+j+1]) break;
				if(j*2+2>max)          
				{ max=j*2+2; x=p[i-j]; y=p[i+j+1]; }
			}
		}
		for(i=x;i<=y;i++)	//把最长回文子串输出
			printf("%c",buf[i]);
		printf("\n");
		return 0;
}

算法分析二:

枚举回文串的“中间”位置i,然后不断往外扩展,直到有字符不同。提示:长度为奇数和偶数的处理方式是不一样的。

#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
	char str[5005]={0},strc[5005]={0};
	int len,_max=-1,x=0,y=0,cnt=0,num[5005]={0};
	gets(str);
	len=strlen(str);
	for(int i=0;i<len;i++)
	{
		if(isalpha(str[i]))
		{
			strc[cnt]=tolower(str[i]);
			num[cnt++]=i;
		}
	}
	for(int i=0;i<cnt;i++)
	{
		for(int j=0;i-j>=0&&i+j<cnt;j++)
		{
			if(strc[i-j]==strc[i+j])
			{
				if(j*2+1>_max)	
				{
					_max=j*2+1;
					x=i-j;
					y=i+j;
				}
			}
			else
				break;
		}		
		for(int j=0;i-j>=0&&i+j+1<cnt;j++)
		{
			if(strc[i-j]==strc[i+j+1])
			{
				if(j*2+2>_max)	
				{
					_max=j*2+2;
					x=i-j;
					y=i+j+1;					
				}
			}
			else
				break;
		}		
	}
	for(int i=num[x];i<=num[y];i++)
	{
		printf("%c",str[i]);
	}
	return 0;
}


发布了33 篇原创文章 · 获赞 14 · 访问量 639

猜你喜欢

转载自blog.csdn.net/Simple_questions/article/details/104961732