本文转自:http://www.cnblogs.com/TenosDoIt/p/3675788.html

题目链接

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

求字符串的最长回文子串

算法1:暴力解法,枚举所有子串,对每个子串判断是否为回文,复杂度为O(n^3)


算法2:删除暴力解法中有很多重复的判断。很容易想到动态规划,时间复杂度O(n^2),空间O(n^2),动态规划方程如下:

  • dp[i][j] 表示子串s[i…j]是否是回文
  • 初始化:dp[i][i] = true (0 <= i <= n-1);  dp[i][i-1] = true (1 <= i <= n-1); 其余的初始化为false
  • dp[i][j] = (s[i] == s[j] && dp[i+1][j-1] == true)

在动态规划中保存最长回文的长度及起点即可

 class Solution {
public:
string longestPalindrome(string s) {
const int len = s.size();
if(len <= )return s;
bool dp[len][len];//dp[i][j]表示s[i..j]是否是回文
memset(dp, , sizeof(dp));
int resLeft = , resRight = ;
dp[][] = true;
for(int i = ; i < len; i++)
{
dp[i][i] = true;
dp[i][i-] = true;//这个初始化容易忽略,当k=2时要用到
}
for(int k = ; k <= len; k++)//枚举子串长度
for(int i = ; i <= len-k; i++)//枚举子串起始位置
{
if(s[i] == s[i+k-] && dp[i+][i+k-])
{
dp[i][i+k-] = true;
if(resRight-resLeft+ < k)
{
resLeft = i;
resRight = i+k-;
}
}
}
return s.substr(resLeft, resRight-resLeft+);
}
};

算法3:以某个元素为中心,分别计算偶数长度的回文最大长度和奇数长度的回文最大长度。时间复杂度O(n^2),空间O(1)

 class Solution {
public:
string longestPalindrome(string s) {
const int len = s.size();
if(len <= )return s;
int start, maxLen = ;
for(int i = ; i < len; i++)
{
//寻找以i-1,i为中点偶数长度的回文
int low = i-, high = i;
while(low >= && high < len && s[low] == s[high])
{
low--;
high++;
}
if(high - low - > maxLen)
{
maxLen = high - low -;
start = low + ;
} //寻找以i为中心的奇数长度的回文
low = i- ; high = i + ;
while(low >= && high < len && s[low] == s[high])
{
low--;
high++;
}
if(high - low - > maxLen)
{
maxLen = high - low -;
start = low + ;
}
}
return s.substr(start, maxLen);
}
};

算法4:Manacher算法,时间复杂度O(n), 空间复杂度O(n)

该算法首先对字符串进行预处理,在字符串的每个字符前后都加入一个特殊符号,比如字符串 abcd 处理成 #a#b#c#d#,为了避免处理越界,在字符串首尾加上不同的两个特殊字符(c类型的字符串尾部不用加,因为自带‘\0’),这样预处理后最终变成$#a#b#c#d#^,经过这样处理后有个好处是原来的偶数长度和奇数长度的回文在处理后的字符串中都是奇数长度。假设处理后的字符串为s                    本文地址

对于已经预处理好的字符串我们用数组p[i]来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),以字符串“12212321”为例,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正好是原字符串中回文串的总长度, 如果p数组已知,遍历p数组找到最大的p[i]就可以求出最长回文的长度,也可以求出回文的位置

下面给出求p[]数组的方法:

设id是当前求得的最长回文子串中心的位置,mx为当前最长回文子串的右边界(回文子串不包括该右边界),即mx = id + p[id]。记j = 2*id – i ,即 j 是 i 关于 id 的对称点。

1、 当i < mx 时,如下图。此时可以得出一个非常神奇的结论p[i] >= min(p[2*id - i], mx - i),下面我们来解释这个结论

如何根据p[j]来求p[i]呢,又要分成两种情况

(1.1)当mx – i > p[j], 这时候以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以 P[i]  至少等于 p[j], 后面的再继续匹配。如下图

注:这里其实p[i]一定等于p[j],后面不用再匹配了。因为如果p[i]后面还可以继续匹配,根据对称性,p[j]也可以继续扩展了

(1.2)当mx – i <= p[j], 以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] 至少等于 mx - i,至于mx之后的部分是否对称,就只能老老实实去匹配了。

注:如果mx – i < p[j] ,这时p[i]一定等于mx - i, 因为如果p[i]在mx之后还可以继续匹配,根据对称性,mx之后匹配的点(包括mx)一定会出现在my的前面,这说明p[id]也可以继续扩展了

2、当i >= mx, 无法对p[i]做更多的假设,只能p[i] = 1,然后再去匹配

算法复杂度分析:根据斜体字部分的注释,只有当mx-i = p[j]时 以及 i > mx时才要扩展比较,而mx也是在不断扩展的,总体而言每个元素比较次数是n的线性关系,所以时间复杂度为O(n)

 class Solution {
public:
string longestPalindrome(string s) {
const int len = s.size();
if(len <= )return s;
//Manncher算法 ,o(n)
string str = preProcess(s);
int n = str.size(), id = , mx = ;
vector<int>p(n, );
for(int i = ; i < n-; i++)
{
p[i] = mx > i ? min(p[*id-i], mx-i) : ;
//if(mx <= i || (mx > i && p[2*id-i] == mx - i)) //根据正文斜体部分的注释,这里可要可不要
while(str[i+p[i]] == str[i-p[i]])p[i]++;
if(i + p[i] > mx)
{
mx = i + p[i];
id = i;
}
} //遍历p,寻找最大回文长度
int maxLen = , index = ;
for(int i = ; i < n-; i++)
if(p[i] > maxLen)
{
maxLen = p[i];
index = i;
}
return s.substr((index - maxLen)/, maxLen-);
}
//预处理字符串,abc预处理后变成$#a#b#c#^
string preProcess(const string &s)
{
int n = s.size();
string res;
res.push_back('$');//把$放到字符串头部
res.push_back('#');//以#作为原来字符串中每个字符的间隔
for(int i = ; i < n; i++)
{
res.push_back(s[i]);
res.push_back('#');
}
res.push_back('^');//以^作为字符串的结尾
return res;
}
};


算法5:可以用后缀数组来解,在源字符串后面加一个特殊字符,然后把源字符串的反转串连接到源字符串后面,那么问题就变成了求这个新字符串的某两个后缀的最长公共前缀,该问题是典型的后缀数组应用,可以参考here,关于后缀数组请关注我的后续博客

参考资料:

Longest Palindromic Substring Part II

Manacher's ALGORITHM: O(n)时间求字符串的最长回文子串

Longest Palindromic Substring | Set 2

转自:http://www.cnblogs.com/TenosDoIt/p/3675788.html

转载:LeetCode:5Longest Palindromic Substring 最长回文子串的更多相关文章

  1. LeetCode:Longest Palindromic Substring 最长回文子串

    题目链接 Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...

  2. Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法)

    Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法) Given a string s, find the longest pal ...

  3. [LeetCode] Longest Palindromic Substring 最长回文串

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  4. [LeetCode] 5. Longest Palindromic Substring 最长回文子串

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  5. 【LeetCode】5. Longest Palindromic Substring 最长回文子串

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:最长回文子串,题解,leetcode, 力扣,python ...

  6. [leetcode]5. Longest Palindromic Substring最长回文子串

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  7. lintcode :Longest Palindromic Substring 最长回文子串

    题目 最长回文子串 给出一个字符串(假设长度最长为1000),求出它的最长回文子串,你可以假定只有一个满足条件的最长回文串. 样例 给出字符串 "abcdzdcab",它的最长回文 ...

  8. 5. Longest Palindromic Substring(最长回文子串 manacher 算法/ DP动态规划)

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  9. 转载-----Java Longest Palindromic Substring(最长回文字符串)

    转载地址:https://www.cnblogs.com/clnchanpin/p/6880322.html 假设一个字符串从左向右写和从右向左写是一样的,这种字符串就叫做palindromic st ...

随机推荐

  1. .net core 2.2跨域session

    不说废话,直奔主题! 使用场景:微服务.前后端已经跨域.又不得不使用session 使用session的好处:此处省略若干字...... 做法: 客户端:该怎么写就怎么写. 服务端: Startup类 ...

  2. windows2012服务器搭建mongodb并设置远程访问

    因为python脚本需要用到mongodb,而且需要本地查看数据库,所以就在腾讯云的windows服务器上部署了mongodb服务器,因为网上大部分教程是针对linux的自己搜索走了很多坑,这里记录下 ...

  3. 黑马基础阶段测试题:定义一个int类型的数组,数组中元素为{5,7,3,9,4}。求出数组中的最小值,并判断最小值是否为偶数,如果是偶数则输出“最小值为偶数”,如果不是偶数则输出“最小值为奇数”。打印如下:

    package com.swift; import java.util.Arrays; public class ArrayTest { public static void main(String[ ...

  4. 关于小程序button控件上下边框的显示和隐藏问题

    问题: 小程序的button控件上下有一条淡灰色的边框,在空件上加上了样式 border:(none/0); 都没办法让button上下的的边框隐藏: 代码如下 <button class=&q ...

  5. Ubuntu 14.04 LTS 触摸板无法使用

    c16b上,触摸板不能使用,查找后发现,需要在加载驱动时增加参数. 如下所说: 1.使用以下命令后,触摸板可以使用 sudo modprobe -r psmouse sudo modprobe psm ...

  6. PHP添加扩展模块的方法

    进入源码包对应扩展模块目录下 ##extname 代表扩展模块名 cd /usr/local/src/php-5.5.36/ext/extname 然后执行phpize##phpize是一个shell ...

  7. 如何用eclipse运行导入的maven项目

    1.配置jdk系统环境变量.找到安装的jdk的安装目录,新建系统环境变量,变量名为JAVA_HOME(作为一个引用),变量值为该路径. 找到Path,将%JAVA_HOME%/bin; 添加到变量值的 ...

  8. hive数据的导入导出方式

    导入方式 1.load方式 load data local inpath 'local_path' into table tb_name; 从本地复制了文件到表的路径下 应用场景:大部分的使用,文件几 ...

  9. Redis之String类型操作

    接口IRedisDaoStr: package com.net.test.redis.base.dao; import java.util.List; import java.util.Map; /* ...

  10. Louis Armstrong【路易斯·阿姆斯特朗】

    Louis Armstrong Louis Armstrong had two famous nicknames. 路易斯·阿姆斯特朗有两个著名的绰号. Some people called him ...