最长回文子串

题目描述

给定一个字符串,求它的最长回文子串的长度。

分析与解法

最容易想到的办法是枚举所有的子串,分别判断其是否为回文。这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的。

解法一

那么如何高效的进行判断呢?我们想想,如果一段字符串是回文,那么以某个字符为中心的前缀和后缀都是相同的,例如以一段回文串“aba”为例,以b为中心,它的前缀和后缀都是相同的,都是a。

那么,我们是否可以可以枚举中心位置,然后再在该位置上用扩展法,记录并更新得到的最长的回文长度呢?答案是肯定的,参考代码如下:

int LongestPalindrome(const char *s, int n)

{

int i, j, max,c;

if (s == 0 || n < 1)

return 0;

max = 0;

for (i = 0; i < n; ++i) { // i is the middle point of the palindrome

for (j = 0; (i - j >= 0) && (i + j < n); ++j){ // if the length of the palindrome is odd

if (s[i - j] != s[i + j])

break;

c = j * 2 + 1;

}

if (c > max)

max = c;

for (j = 0; (i - j >= 0) && (i + j + 1 < n); ++j){ // for the even case

if (s[i - j] != s[i + j + 1])

break;

c = j * 2 + 2;

}

if (c > max)

max = c;

}

return max;

}

代码稍微难懂一点的地方就是内层的两个 for 循环,它们分别对于以 i 为中心的,长度为奇数和偶数的两种情况,整个代码遍历中心位置 i 并以之扩展,找出最长的回文。

解法二、O(N)解法

在上文的解法一:枚举中心位置中,我们需要特别考虑字符串的长度是奇数还是偶数,所以导致我们在编写代码实现的时候要把奇数和偶数的情况分开编写,是否有一种方法,可以不用管长度是奇数还是偶数,而统一处理呢?比如是否能把所有的情况全部转换为奇数处理?

答案还是肯定的。这就是下面我们将要看到的Manacher算法,且这个算法求最长回文子串的时间复杂度是线性O(N)的。

首先通过在每个字符的两边都插入一个特殊的符号,将所有可能的奇数或偶数长度的回文子串都转换成了奇数长度。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。

此外,为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。

以字符串12212321为例,插入#和$这两个特殊符号,变成了 S[] = “$#1#2#2#1#2#3#2#1#”,然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左或向右扩张的长度(包括S[i])。

比如S和P的对应关系:

S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #

P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1

可以看出,P[i]-1正好是原字符串中最长回文串的总长度,为5。

接下来怎么计算P[i]呢?Manacher算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。得到一个很重要的结论:

如果mx > i,那么P[i] >= Min(P[2 * id – i], mx – i)

C代码如下:

//mx > i,那么P[i] >= MIN(P[2 * id - i], mx - i)

//故谁小取谁

if (mx - i > P[2*id - i])

P[i] = P[2*id - i];

else //mx-i <= P[2*id - i]

P[i] = mx - i;

下面,令j = 2*id – i,也就是说j是i关于id的对称点。

当 mx – i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于i和j对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有P[i] = P[j];

当 P[j] >= mx – i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx – i。至于mx之后的部分是否对称,再具体匹配。

此外,对于 mx <= i 的情况,因为无法对 P[i]做更多的假设,只能让P[i] = 1,然后再去匹配。

综上,关键代码如下:

//输入,并处理得到字符串s

int p[1000], mx = 0, id = 0;

memset(p, 0, sizeof(p));

for (i = 1; s[i] != '\0'; i++)

{

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

while (s[i + p[i]] == s[i - p[i]])

p[i]++;

if (i + p[i] > mx)

{

mx = i + p[i];

id = i;

}

}

//找出p[i]中最大的

此Manacher算法使用id、mx做配合,可以在每次循环中,直接对P[i]的快速赋值,从而在计算以i为中心的回文子串的过程中,不必每次都从1开始比较,减少了比较次数,最终使得求解最长回文子串的长度达到线性O(N)的时间复杂度。

统计5个字符串回文个数c语言,第一章 字符串 – 1.5 最长回文子串 - 编程之法:面试和算法心得...相关推荐

  1. C语言试题三十三之比较两个字符串的长度,(不得调用c语言提供的求字符串长度的函数),函数返回较长的字符串。若两个字符串长度相同,则返回第一个字符串。

    1. 题目 请编写函数function,它的功能是:比较两个字符串的长度,(不得调用c语言提供的求字符串长度的函数),函数返回较长的字符串.若两个字符串长度相同,则返回第一个字符串. 2 .温馨提示 ...

  2. 习题 5.12 编写一程序,将两个字符串连接起来,结果取代第一个字符串。

    C++程序设计(第三版) 谭浩强 习题5.12 个人设计 习题 5.12 编写一程序,将两个字符串连接起来,结果取代第一个字符串. (1)用字符数组,不用strcat函数(即自己写一个具有strcat ...

  3. c语言从4个整数中找最小的数,编程之法:面试和算法心得(寻找最小的k个数)...

    内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java 题目描述 输入n个整数,输出其中最小的k个. 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个 ...

  4. 课后习题5.13 编写一程序,将两个字符串连接起来,结果取代第一个字符串。 (1)用字符数组,不用stract函数(即自己写一个具有stract函数功能的函数); (2)用标准库中的stract函数;

    课后习题5.13 编写一程序,将两个字符串连接起来,结果取代第一个字符串. (1)用字符数组,不用stract函数(即自己写一个具有stract函数功能的函数): (2)用标准库中的stract函数: ...

  5. 最长回文串_LeetCode解析,第五题:最长回文子串

    LeetCode第五题:最长回文子串 5: 英文题面: Given a string s, find the longest palindromic substring in s. You may a ...

  6. c语言九三字符串的字母个数,C语言判断闰年和平年

    C语言判断闰年和平年 #includevoid fun(int year) { if((year%4==0&&year%100!=0)||(year%100==0&&y ...

  7. 切割字符串长度php,C++_C语言中计算字符串长度与分割字符串的方法,C语言strlen()函数:返回字符串 - phpStudy...

    C语言中计算字符串长度与分割字符串的方法 C语言strlen()函数:返回字符串的长度头文件: #include strlen()函数用来计算字符串的长度,其原型为: unsigned int str ...

  8. c语言中有裁剪字符串的函数吗,C语言中的字符串截取函数

    /*======================================================== 子数整数 源程序名 num.??? (pas,c,cpp) 可执行文件名 num. ...

  9. c语言字符串数组的合并,C语言实现合并字符串

    学会Perl以及Python之后,处理字符串也只是我很喜欢做的一件事情.进行字符串的拼接在这些高级脚本语言中是一件轻松的事情. C语言是我的编程入门语言,但是我一直对这门语言了解并不是很深.确切说,我 ...

最新文章

  1. 字符串声明太大出现错误_搜索框输入中文出现单引号报错问题
  2. Android base64 上传图片
  3. Uber发布史上最简单的深度学习框架Ludwig!
  4. Attribute “singleton” must be declared for element type “bean”.
  5. 作者:孙大为,男,博士后,中国地质大学(北京)信息工程学院讲师。
  6. python中findroot_Python源码问题算负数平方根无结果输出何解,python负数,def findRoot...
  7. [Linux][Ubuntu][14.04.3LTS]安装NVidia显卡驱动
  8. MyEclipse 使用文档
  9. IE 9 beta 下载地址
  10. 写给零基础入坑蓝桥杯的同学
  11. 魔界/指环王三部曲(加长版)在线观看免费bt下载
  12. 要管理此计算机上的用户账户,Win10安装软件用户账户控制提示管理员已阻止运行此应用解决方法...
  13. 论文Hierarchical Chamfer Matching
  14. python作业:学生成绩表数据包括:学号、姓名,高数、英语和计算机3门课成绩
  15. was not registered for synchronization because错误
  16. python光棍节快乐_2020年光棍节快乐的祝福语5条
  17. 区块链技术与应用实验报告(实验五)
  18. 生命计算器 --算算已经活了多少天了
  19. 用代码和想象力拥抱一个物联网时代
  20. matlab中break和continue

热门文章

  1. Oracle 20c 新特性:自动的区域图 - Automatic Zone Maps
  2. 验证GaussDB T 闪回事务查询功能;闪回表功能强劲闪回TRUNCATE
  3. 云图说 | 3分钟创建一个游戏类工作负载
  4. 快速了解云原生中的微服务应用(内含福利)
  5. 【华为云技术分享】气象模拟WRF容器化操作实践
  6. 设计模式的C语言应用-非典型模式-第十章
  7. ngnix 执行php 慢,【nginx】PHP有什么办法可以排查那些脚本或方法执行慢,需要优化?...
  8. Android doc|Getting Started|部分 --转载 保存数据
  9. LeetCode-978:最长湍流子数组
  10. CenterNet+ deepsort实现多目标跟踪