最长回文子串

来源:互联网 发布:2016年nba总决赛g4数据 编辑:程序博客网 时间:2024/06/16 09:54

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

样例输入:Confuciuss say:Madam,I’m Adam.

样例输出:Madam,I’m Adam

本题算法思想:
如果输入的全部都是大写字母,问题就简单了:直接判断每个子串即可。可其他字符的出现把问题弄得复杂了。首先我们不能用scanf(“%s”)输入字符串,因为它碰到空格或者TAB就会停下来。
这里我们使用fgets来解决问题。

这里写图片描述
其中buf声明为char buf[MAXN]。fgets函数读取不超过MAXN-1个字符,然后在末尾添上结束符‘\0’,因此不会出现越界的情况。之所以说可以用这个函数读取完整的一行,是因为一旦读到回车符‘\n’,读取工作将会停止,而这个‘\n’也会是buf字符串中最后一个有效字符(再往后就是字符串结束符‘\0’了)。只有在一种情况下,buf不会以‘\n’结尾:读到文件结束符,并且文件的最后一个不是以‘\n’结尾。fin是指文件fin,即fgets是从打开的文件fin中读取的。

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

代码1:从前往后遍历字符串找出回文串。尚未完成版!还应增加一个数组p,用p[i]保存s[i]在buf中的位置—为了原样输出。它可以很容易的在预处理中得到,然后在更新max的同时把p[i]和p[j]保存到x和y,最后输出buf[x]到buf[y]中的所有字符。不过这样程序效率有些低,因此考虑写出代码2中的方法。

#include<iostream>#include<stdio.h>#include<stdlib.h>#include<math.h>#include<algorithm>#include<string.h>#include<ctype.h>     //用到isalpha、touuper等工具  using namespace std;#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]是否为回文串;注意这里的循环变量不能是i或j,因为它们已经在外层用过了。s[k]的"对称"位置是s[i+j-k] !                  if(s[k]!=s[i+j-k]) ok=0;   //只要一次比较失败,就应把标记ok置为0               if(ok && j-i+1>max) max=j-i+1;   //max保存当前发现的最长回文串的长度,j-i+1是当前回文串的长度         }          printf("max=%d\n",max);          return 0;  }  

代码2:从中间往两边遍历字符串找出回文串;即枚举回文串的“中间”位置i,然后不断往外扩展,直到有字符不同。 注意:长度为奇数和偶数的处理方式是不一样的。 (代码2是完整的)

#include<iostream>#include<stdio.h>#include<stdlib.h>#include<math.h>#include<algorithm>#include<string.h>#include<ctype.h>     //用到isalpha、touuper等工具  using namespace std;#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;  } 
原创粉丝点击