LeetCode:Longest Palindromic Substring 最长回文子串
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 <= 1)return s;
- bool dp[len][len];//dp[i][j]表示s[i..j]是否是回文
- memset(dp, 0, sizeof(dp));
- int resLeft = 0, resRight = 0;
- dp[0][0] = true;
- for(int i = 1; i < len; i++)
- {
- dp[i][i] = true;
- dp[i][i-1] = true;//这个初始化容易忽略,当k=2时要用到
- }
- for(int k = 2; k <= len; k++)//枚举子串长度
- for(int i = 0; i <= len-k; i++)//枚举子串起始位置
- {
- if(s[i] == s[i+k-1] && dp[i+1][i+k-2])
- {
- dp[i][i+k-1] = true;
- if(resRight-resLeft+1 < k)
- {
- resLeft = i;
- resRight = i+k-1;
- }
- }
- }
- return s.substr(resLeft, resRight-resLeft+1);
- }
- };
算法3:以某个元素为中心,分别计算偶数长度的回文最大长度和奇数长度的回文最大长度。时间复杂度O(n^2),空间O(1)
- class Solution {
- public:
- string longestPalindrome(string s) {
- const int len = s.size();
- if(len <= 1)return s;
- int start, maxLen = 0;
- for(int i = 1; i < len; i++)
- {
- //寻找以i-1,i为中点偶数长度的回文
- int low = i-1, high = i;
- while(low >= 0 && high < len && s[low] == s[high])
- {
- low--;
- high++;
- }
- if(high - low - 1 > maxLen)
- {
- maxLen = high - low -1;
- start = low + 1;
- }
- //寻找以i为中心的奇数长度的回文
- low = i- 1; high = i + 1;
- while(low >= 0 && high < len && s[low] == s[high])
- {
- low--;
- high++;
- }
- if(high - low - 1 > maxLen)
- {
- maxLen = high - low -1;
- start = low + 1;
- }
- }
- 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 <= 1)return s;
- //Manncher算法 ,o(n)
- string str = preProcess(s);
- int n = str.size(), id = 0, mx = 0;
- vector<int>p(n, 0);
- for(int i = 1; i < n-1; i++)
- {
- p[i] = mx > i ? min(p[2*id-i], mx-i) : 1;
- //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 = 0, index = 0;
- for(int i = 1; i < n-1; i++)
- if(p[i] > maxLen)
- {
- maxLen = p[i];
- index = i;
- }
- return s.substr((index - maxLen)/2, maxLen-1);
- }
- //预处理字符串,abc预处理后变成$#a#b#c#^
- string preProcess(const string &s)
- {
- int n = s.size();
- string res;
- res.push_back('$');//把$放到字符串头部
- res.push_back('#');//以#作为原来字符串中每个字符的间隔
- for(int i = 0; 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:Longest Palindromic Substring 最长回文子串的更多相关文章
- Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法)
Leetcode 5. Longest Palindromic Substring(最长回文子串, Manacher算法) Given a string s, find the longest pal ...
- [LeetCode] Longest Palindromic Substring 最长回文串
Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...
- [LeetCode] 5. Longest Palindromic Substring 最长回文子串
Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...
- 【LeetCode】5. Longest Palindromic Substring 最长回文子串
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:最长回文子串,题解,leetcode, 力扣,python ...
- [leetcode]5. Longest Palindromic Substring最长回文子串
Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...
- lintcode :Longest Palindromic Substring 最长回文子串
题目 最长回文子串 给出一个字符串(假设长度最长为1000),求出它的最长回文子串,你可以假定只有一个满足条件的最长回文串. 样例 给出字符串 "abcdzdcab",它的最长回文 ...
- 5. Longest Palindromic Substring(最长回文子串 manacher 算法/ DP动态规划)
Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...
- 转载:LeetCode:5Longest Palindromic Substring 最长回文子串
本文转自:http://www.cnblogs.com/TenosDoIt/p/3675788.html 题目链接 Given a string S, find the longest palindr ...
- 1. Longest Palindromic Substring ( 最长回文子串 )
要求: Given a string S, find the longest palindromic substring in S. (从字符串 S 中最长回文子字符串.) 何为回文字符串? A pa ...
随机推荐
- java端口扫描(原创)
项目需要扫描占用的端口来判断服务是否启动,通过查资料发现大多数方法都是ServerSocket socket = new ServerSocket(port);代码如下: package com.fr ...
- [HNOI2008],[bzoj1008] 越狱(dp+组合数学)
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1008 Description 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人 ...
- poj3160 强连通+记忆化搜索
题意:有一张 n 点 m 边的有向无环图,每个点有各自的权值,可正可负,现在从一个点开始走,一直走到不能走到其他点为止,每经过一个点,可以选择获得或不获得它的权值,每个点可以走多次,但是权值只能获得一 ...
- 全面理解Javascript中Function对象的属性和方法
http://www.cnblogs.com/liontone/p/3970420.html 函数是 JavaScript 中的基本数据类型,在函数这个对象上定义了一些属性和方法,下面我们逐一来介绍这 ...
- Sea.js学习2——Sea.js的API 快速参考
(7 个接口是最常用的) 一.seajs.config:用来对 Sea.js 进行配置. seajs.config({ // 设置路径,方便跨目录调用 paths: { 'arale': 'https ...
- Autocad 常用命令
一律使用的简写 1:将多条线段合并成一条多线段[pe] 2:如果发现合并的多线段将自己不想合并的区域合并进去了,别担心,炸开就行了 [x] 3:如果画的线段太长工作区不适应,使用缩放命令.[scale ...
- CentOS 7 httpd服务的安装、配置
关于linux httpd的搭建和配置都需要基于yum源 1:安装httpd yum linstall httpd -y 2:进入html文件 cd /var/www/html 讲已经编辑的htm ...
- bootstrap初接触
Bootstrap 是最受欢迎的 HTML.CSS 和 JS 框架.(主要是结合HTML5 CSS3的样式, 基于jquery+Bootstrap 自带 12 种 jQuery 插件,扩展了功能,可以 ...
- JS教程:获取当前地址栏URL
URL即:统一资源定位符 (Uniform Resource Locator, URL) 完整的URL由这几个部分构成:scheme://host:port/path?query#fragment s ...
- C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)
1. 主版本模板类 首先我们来看一段初学者都能看懂,应用了模板的程序: 1 #include <iostream> 2 using namespace std; 3 4 template ...