ps.此贴大部分文字与代码来自网上,我只是取长补短整理了下

S=“c a b a”  那么  S' = “a b a c”, 这样的情况下 S和 S‘的最长公共子串是aba。没有错误。

但是当 S=“abacdfgdcaba”, 那么S’ = “abacdgfdcaba”。 这样S和S‘的最长公共子串是abacd。很明显abacd并不是S的最长回文子串,它甚至连回文都不是。

现在是不是都明白为什么最长回文子串不能转化成为最长公共子串问题了。当原串S中含有一个非回文的串的反序串的时候,最长公共子串的解法就是不正确的。正如上一个例子中S既含有abacd,又含有abacd的反串cdaba,并且abacd又不是回文,所以转化成为最长公共子串的方法不能成功。除非每次我们求出一个最长公共子串的时候,我们检查一下这个子串是不是一个回文,如果是,那这个子串就是原串S的最长回文子串;如果不是,那么就去求下一个次长公共子串,以此类推。

最长回文子串有很多方法,分别是1暴力法,2 动态规划, 3 从中心扩展法,4 著名的manacher算法。下面我将分别介绍几种方法。

方法一 暴力法

遍历字符串S的每一个子串,去判断这个子串是不是回文,是回文的话看看长度是不是比最大的长度maxlength大。遍历每一个子串的方法要O(N2),判断每一个子串是不是回文的时间复杂度是O(N),所以暴利方法的总时间复杂度是O(N3)。

 

方法二 动态规划 时间复杂度O(N2), 空间复杂度O(N2)

动态规划就是暴力法的进化版本,我们没有必要对每一个子串都重新计算,看看它是不是回文。我们可以记录一些我们需要的东西,就可以在O(1)的时间判断出该子串是不是一个回文。这样就比暴力法节省了O(N)的时间复杂度哦,嘿嘿,其实优化很简单吧。

P(i,j)为1时代表字符串Si到Sj是一个回文,为0时代表字符串Si到Sj不是一个回文。

P(i,j)= P(i+1,j-1)(如果S[i] = S[j])。这是动态规划的状态转移方程。

P(i,i)= 1,P(i,i+1)= if(S[i]= S[i+1])

string longestPalindromeDP(string s) {int n = s.length();int longestBegin = 0;int maxLen = 1;bool table[1000][1000] = {false};for (int i = 0; i < n; i++) {table[i][i] = true;   //前期的初始化
}for (int i = 0; i < n-1; i++) {if (s[i] == s[i+1]) {table[i][i+1] = true; //前期的初始化
longestBegin = i;maxLen = 2;}}for (int len = 3; len <= n; len++) {for (int i = 0; i < n-len+1; i++) {int j = i+len-1;if (s[i] == s[j] && table[i+1][j-1]) {table[i][j] = true;longestBegin = i;maxLen = len;}}}return s.substr(longestBegin, maxLen);}

View Code

方法三 中心扩展法

这个算法思想其实很简单啊,时间复杂度为O(N2),空间复杂度仅为O(1)。就是对给定的字符串S,分别以该字符串S中的每一个字符C为中心,向两边扩展,记录下以字符C为中心的回文子串的长度。但是有一点需要注意的是,回文的情况可能是 a b a,也可能是 a b b a。

string expandAroundCenter(string s, int c1, int c2) {int l = c1, r = c2;int n = s.length();while (l >= 0 && r <= n-1 && s[l] == s[r]) {l--;r++;}return s.substr(l+1, r-l-1);}string longestPalindromeSimple(string s) {int n = s.length();if (n == 0) return "";string longest = s.substr(0, 1);  // a single char itself is a palindromefor (int i = 0; i < n-1; i++) {string p1 = expandAroundCenter(s, i, i);if (p1.length() > longest.length())longest = p1;string p2 = expandAroundCenter(s, i, i+1);if (p2.length() > longest.length())longest = p2;}return longest;}

View Code

  方法四 传说中的Manacher算法。时间复杂度O(N)

这个算法有一个很巧妙的地方,它把奇数的回文串和偶数的回文串统一起来考虑了。这一点一直是在做回文串问题中时比较烦的地方。这个算法还有一个很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重复匹配。

算法大致过程是这样。先在每两个相邻字符中间插入一个分隔符,当然这个分隔符要在原串中没有出现过。一般可以用‘#’分隔。这样就非常巧妙的将奇数长度回文串与偶数长度回文串统一起来考虑了(见下面的一个例子,回文串长度全为奇数了),然后用一个辅助数组P记录以每个字符为中心的最长回文串的信息。P[id]记录的是以字符str[id]为中心的最长回文串,当以str[id]为第一个字符,这个最长回文串向右延伸了P[id]个字符。
    原串:    w aa bwsw f d
    新串:           #  w  # a # a #  b  # w # s # w #  f  # d #
辅助数组P: 1  2  1 2 3 2 1  2  1  2 1 4 1 2 1  2 1 2 1

这里有一个很好的性质,P[id]-1就是该回文子串在原串中的长度(包括‘#’)。如果这里不是特别清楚,可以自己拿出纸来画一画,自己体会体会。当然这里可能每个人写法不尽相同,不过我想大致思路应该是一样的吧。

现在的关键问题就在于怎么在O(n)时间复杂度内求出P数组了。只要把这个P数组求出来,最长回文子串就可以直接扫一遍得出来了。

那么怎么计算P[i]呢?该算法增加两个辅助变量(其实一个就够了,两个更清晰)id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

然后可以得到一个非常神奇的结论,这个算法的关键点就在这里了:如果mx > i,那么

P[i] >= MIN(P[2 * id - i], mx - i)。就是这个串卡了我非常久。实际上如果把它写得复杂一点,理解起来会简单很多:

//记j = 2 * id - i,也就是说 j 是 i 关于 id 的对称点。
if (mx - i > P[j]) 
    P[i] = P[j];
else /* P[j] >= mx - i */
    P[i] = mx - i; // P[i] >= mx - i,取最小值,之后再匹配更新。

当 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之后的部分是否对称,就只能老老实实去匹配了。

由于这个算法是线性从前往后扫的。那么当我们准备求P[i]的时候,i以前的P[j]我们是已经得到了的。我们用mx记在i之前的回文串中,延伸至最右端的位置。同时用id这个变量记下取得这个最优mx时的id值。(注:为了防止字符比较的时候越界,我在这个加了‘#’的字符串之前还加了另一个特殊字符‘$’,故我的新串下标是从1开始的)

#include<vector>
#include<iostream>
using namespace std;const int N=300010;
int n, p[N];
char s[N], str[N];#define _min(x, y) ((x)<(y)?(x):(y))void kp()
{int i;int mx = 0;int id;for(i=n; str[i]!=0; i++)str[i] = 0; //没有这一句有问题。。就过不了ural1297,比如数据:ababa abafor(i=1; i<n; i++){if( mx > i )p[i] = _min( p[2*id-i], p[id]+id-i );elsep[i] = 1;for(; str[i+p[i]] == str[i-p[i]]; p[i]++);if( p[i] + i > mx ){mx = p[i] + i;id = i;}}
}void init()
{int i, j, k;str[0] = '$';str[1] = '#';for(i=0; i<n; i++){str[i*2+2] = s[i];str[i*2+3] = '#';}n = n*2+2;s[n] = 0;
}int main()
{int i, ans;while(scanf("%s", s)!=EOF){n = strlen(s);init();kp();ans = 0;for(i=0; i<n; i++)if(p[i]>ans)ans = p[i];printf("%d\n", ans-1);}return 0;
}

View Code

//模板void Manacher(string s,int len)
{a += '$';a += '#';for(int i=0; i<len; i++){a += s[i];a += '#';}a += (char)0;int mx = 0, id = 0;int l = a.length();for(int i = 0; i < l; i++){p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;while(a[i + p[i]] == a[i - p[i]]) p[i]++;if(i + p[i] > mx){mx = i + p[i];id = i;}}
}
在main里面还需要int ans = 0;for(int i = 0; i < 2 * len + 2; i++)ans = max(ans, p[i] - 1);    

转载于:https://www.cnblogs.com/acbingo/p/4093782.html

1112个人赛,最长回文串常见算法讨论相关推荐

  1. 最长回文串 马拉车算法 C++

    最长回文串 LeetCode 5.最长回文串 给你一个字符串 s,找到 s 中最长的回文子串. 示例 1: 输入:s = "babad" 输出:"bab" 解释 ...

  2. 通俗易懂的最长回文串图解、说明及Java代码(中心扩散法和Manacher算法)

    1. 回文串 作为程序员,回文串这个词已经见怪不怪了,就是一个字符串正着读和反着读是一样的,形式如abcdcba.bbaabb.这里涉及到奇回文和偶回文,奇回文指回文串的字符数是奇数,偶回文指回文串的 ...

  3. java 最长回文串_通俗易懂的最长回文串图解、说明及Java代码(中心扩散法和Manacher算法)...

    1. 回文串 作为程序员,回文串这个词已经见怪不怪了,就是一个字符串正着读和反着读是一样的,形式如abcdcba.bbaabb.这里涉及到奇回文和偶回文,奇回文指回文串的字符数是奇数,偶回文指回文串的 ...

  4. 伍六七带你学算法 入门篇-最长回文串

    力扣解题,每日一题:409. 最长回文串 难度- 简单 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" ...

  5. manacher算法----O(n)最长回文串

    manacher算法----O(n)最长回文串 分类:字符串 (126)  (0)  举报  收藏 manacher的时间复杂度为O(n),后缀数组好像可以处理O(nlogn),但是有些变态题目可能卡 ...

  6. python【力扣LeetCode算法题库】409-最长回文串(数学 计数器)

    最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意: 假设字 ...

  7. Leetcode69场双周赛-第三题5962. 连接两字母单词得到的最长回文串

    5962. 连接两字母单词得到的最长回文串 题目描述 解题思路和解题代码 定义map和sameMap.如果两个字母一样,就看sameMap里面有没有记录,有的话把该记录取出来,结果总数+4,没有的话, ...

  8. 字符串相关处理kmp,前缀数,后缀树,后缀数组,最长回文串,最长重复字串,最长非重复字串

    1. 最长回文串 一般用后缀数组或者后缀树可以解决, 用此方法:http://blog.csdn.net/v_july_v/article/details/6897097 预处理后缀树,使得查询LCA ...

  9. 最长回文串--动态规划

    最长回文串–动态规划 参考:https://writings.sh/post/algorithm-longest-palindromic-substring class Solution {publi ...

最新文章

  1. python字典方法汇总_python字典的操作总结
  2. 九章算法班L8 Array Number
  3. 压缩感知(II) A Compressed Sense of Compressive Sensing (II)
  4. 鸟哥的Linux私房菜(基础篇)-第四章、安装 CentOS 5.x 与多重开机小技巧(三.1. 本练习机的规划--尤其是分割参数)
  5. python中的函数定义问题
  6. ajax请求flask以后得到的响应查看
  7. [渝粤教育] 三江学院 软件测试 参考 资料
  8. linux下编译安装ntfs,linux下编译安装ntfs
  9. EL表达式和JSTL标签库使用
  10. java实现中缀表达式转后缀表达式
  11. 抠图为什么要用绿布_逆天抠图,Photoshop 2020天神下凡
  12. 关于:XP精简版装IIS 添加/删除windows组件中找不到IIS
  13. su如何变成实体_Sketchup实体工具怎么使用? SU实体工具的使用方法
  14. SEOer必读:50个网站推广方法
  15. 一张图带你看完图论第一章(包含定义、定理、公式、推导证明和例题)
  16. python的取整函数:向上取整,向下取整,四舍五入取整
  17. Dicky - 开源中国社区
  18. c语言单招程序设计解析,学生教学论文,关于单招C语言程序设计教学方法探究相关参考文献资料-免费论文范文...
  19. wind10MySQL闪退什么密码_小编调解技术编辑应对win10系统Mysql输入密码后闪退的操作办法的解决教程...
  20. 微信小程序 - 入门篇

热门文章

  1. ConstantSourceNode
  2. go语言 slice
  3. requests session
  4. C++ 构造函数和析构函数
  5. 机器学习 正则化(regularization)
  6. 计算机丢失winmm.dll,winmm.dll
  7. css中英文混排 标点,浅析css中英文混排时行高不一样的原因及其解决方法
  8. pgsql函数定时更新表_Postgresql PL/PGSQL 程序语言系列 1 (存储过程过时了吗,与函数)...
  9. find和grep命令合集
  10. Linux 更改共享内存tmpfs的大小