一.题目链接:https://leetcode.com/problems/longest-palindromic-substring/

二.题目大意:

  给定一个字符串,找出它最长的回文子串。例如,字符串“caabb”,它的最长回文子串为“aabb”。

三.题解:

  找最长回文子串应该说是比较经典的题目,这个题目我目前有三种思路:

方法1:暴力解决,找出所有的子串,并判断子串是不是回文,然后记录最长的回文子串。代码如下:

class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
string rs;//用于保存最长的回文子串
int max_len = 0;
for(int i = 0;i < len;i++)
for(int j = i + 1;j <= len;j++)
{
string temp = s.substr(i,j - i); if(isPalin(temp) == true)
{
if(max_len < temp.size())
{
max_len = temp.size();
rs = temp;
}
}
}
return rs;
}
bool isPalin(string s)
{
int len = s.length();
int flag = 1;
for(int i = 0;i < len;i++)
{
if(s[i] != s[len - 1 - i])
flag = 0;
}
if(flag)
return true;
return false;
} };

 其中遍历所有的子串需要的时间复杂度为O(n2),判断子串是不是回文串的时间复杂度为O(n),所以总的时间复杂度为O(n3)。

提交结果:Time Limit Exceeded(超时).

方法2:

  以字符串中的每个字符为中心向两边扩展,从而找到最长的回文子串。其中回文子串存在两种情况:(1)形如"aabaa"这种中间只有一个字符的回文子串。(2)形如"aabbaa"这种中间有两个或多个字符的回文子串。所以在处理的时候,先优考虑第二种情况,可以吧第二中情况中的中间重复字符看成一个字符,然后剩下的部分同第一种情况就可以进行相同的处理了。代码如下:

class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
int max_len = 0;
string rs = "";
for(int i = 0;i < len;i++)
{
int f = i,b = i;
int df = 0;
int oddf = 0;
while(s[b] == s[i])//中间字符存在重复的情况
{
b++;
oddf = 1;
}
if(oddf == 1)
b--;
while(f >= 0 && b < len && s[f] == s[b])//中间字符的左右两边字符相等,两边都增长
{
f--;
b++;
df = 1;
}
if(df == 1)
{
f++;
b--;
}
string temp = s.substr(f,b-f+1);
if(max_len < temp.size())
{
max_len = temp.size();
rs = temp;
} }
return rs;
}
};

  

这个程序中的我设置了两个哨兵,用来判断是否发生了以下情况:

(1)中间字符连续几个都相同或重复,此时下标b增加。

(2)中间字符的左右两边字符相等,所以两边都增长,此时b增加,f减小。

由于每次增长后,下标f(或b)都会再次-1(+1),相当于多减少(增加)了一次,所以需要增加(减少)一次,来恢复成为正常的下标。 

这种方法的时间复杂度为O(n2),空间复杂度为O(n)。

提交结果:Accepted(16ms).

方法3:

  利用动态规划的思想,将父问题拆分为若干个子问题,用dp[i][j]来表示字符串下标为[i,j]的子串是否为回文串,那么有以下的分析:

(1)如果i == j表示子串是一个字符,那么此时必然是一个回文串。

(2)如果相邻的字符相等(这种情况实质就是方法2中的中间字符重复的情况),j == i+1,此时要判断s[i]与s[j]是否相等,如果相等,那么该子串也是回文串,

(3)判断剩下的情况,如果(s[i],s[i+1],.....,s[j])为回文串的话,那么(s[i+1],s[i+2],.....s[j-1])必然也是一个回文串,且s[i] == s[j]。

所以,初始状态为:

dp[i][i] =1。

整个状态方程为:

代码如下:

class Solution
{
public:
string longestPalindrome(string s)
{
int len = s.size();
string rs = "1";
int start = 0;
int max_len = 1;//此处的初始值为1,默认为dp[i][i]
int dp[1000][1000]= {0};
for(int i = 0; i < len; i++)
{
dp[i][i] = 1;//初始状态,在求解前必须初始化
for(int j = 0; j < i; j++)
{ if(i == j+1)
{
if(s[i] == s[j])
dp[j][i] = 1;
}
else
{
if(s[i] == s[j])
{
dp[j][i] = dp[j+1][i -1];
}
}
if(dp[j][i] && max_len < (i - j +1))
{
max_len = (i - j +1);
start = j;
}
}
}
rs = s.substr(start,max_len);
return rs;
} };

这种方法有需要注意的几点:

(1)最大长度的初始值为1,而不是0,默认为dp[i][i]这种形式。

(2)由于求解父问题实质,是将父问题分解成为若干个子问题,所以子问题必须被解决,才能求解父问题。所以,一定要利用初始状态对dp进行初始化,即dp[i][i]=1。(动态规划类问题都要初始化初始状态)

(3)在这个程序中,利用dp[j][i]来代替理论上的dp[i][j],这是因为第一层for循环还没取到所有值的时候,第二层for循环已经取了所有可能的值了,这就可能导致子问题还没解决,就去求解父问题了。例如:

求dp[0][4]时,需用到dp[1][3],如果用常规的for循环的话,i=1肯定比i=0出现的晚,导致父问题求解出错。(这可以看成动态规划类问题的常用的技巧)

提交结果:Accepted( 212 ms). 比方法2慢好多...

========================================经查知,还有一种更优的算法============================================================================================================================

方法4:

就是有名的Manacher算法了,感觉这个算法真的不容易想到,这个算法的时间复杂度直接达到了常数级别,即O(n)。理解一下这个算法,对思维的锻炼还是挺不错的。Manacher算法的大致思路如下:

1.预处理:

  (1)对于一个输入的字符串s,把字符串中的任意两个字符之间插入一个"#"(也可以是其他字符),第一个字符之前和最后一个字符之后也要插入。这样就生成了一个新字符串,例如:对于字符串aabbcc,经过处理后,就变成了#a#a#b#b#c#c#。这么做的好处就是不用考虑字符串的奇偶性了,所有的字符串的长度都变成了奇数。

  (2)对于经过第一步处理后的字符串,在该字符串的首位各添加一个字符,但首尾字符一定不能是相同的。(如果是相同的话,在判断最后一个#时,会把这两个字符也考虑进去)。例如:对于#a#a#b#b#c#c#,首位字符分别添加为"%"和"$",则最终经过预处理后的字符串就变成了%#a#a#b#b#c#c#$。这么做的好处是为了防止判断字符串时发生越界。

2.构造一个数组P,其中P[i]表示的是以字符s1[i]为中心最长的回文串向左/向右扩展的长度(包括s[i],可以理解成其"半径"),P数组有一个性质:P[i] - 1是是该回文子串在原字符串中的长度(即在s中的长度)。为什么呢?以下是证明:

原字符串是s经过预处理后,变成了长度为奇数的字符串s1,已知P[i],则以s1[i]为中心的最长回文串的长度为2*p[i] -1(回文子串的长度必然为奇数),其中一定有p[i]个分隔符"#"(观察可知),所以该回文子串在原字符串s中的长度为p[i] - 1。所以求出数组P这个问题基本就解决了。

3.如何求解数组P呢?这里需要引入两个变量,id和mx,其中id表示某个回文串的中心,并且该回文串满足这样一个性质:它右边界是目前所有回文串中最大的。而mx表示的就是这个回文串的右边界的下标。对于数组p,我们可以通过mx和id以及之前已经求出的p[i]的值来求解,具体如下:

先从左至右依次计算P[i],但计算P[i]时,P[j](j<i)已经计算完毕。此时分为两种情况:

(1)i < mx.

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]也可以继续扩展了

所以,最后p[i]去最小值(之后的值要去匹配),即p[i] = min(p[2*id - i], mx - i).

(2)i >= mx.

此时并不能利用已知的信息来求解P[i],此时默认P[i]=1,剩下的部分通过匹配来求解P[i]。

该方法的代码如下:

class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
if(len <= 1)return s;
//对s进行预处理
string temp_str = "#";
for(int i = 0; i < len; i++)
{
temp_str += s[i];
temp_str += "#";
}
temp_str += "^";
string st_str = "";
st_str += "%";
st_str += temp_str;
return Manacher(st_str,s);
}
//马拉车算法,时间复杂度为O(n)
string Manacher(string s1,string s2)
{
int p[3000] = {0};
int id = 0, mx = 0;
int len = s1.size();
int max_len = 0;
int flag = 0;
for(int i = 1; i < len - 1; i++)
{ if(i < mx)
{
if(p[2 * id - i] < (mx - i))
p[i] = p[2 * id - i];
else//即p[2*id-i] >= (mx -i)
p[i] = mx - i;
}
else
{
p[i] = 1;
}
//通过匹配计算p[i]
while(s1[i + p[i]] == s1[i - p[i]])
p[i]++;
//更新mx和id的值
if(p[i] + i > mx)
{
mx = p[i] + i - 1;
id = i;
}
if(p[i] > max_len)
{
max_len = p[i];
flag = i - p[i];
} }
return s2.substr(flag/2, max_len - 1); }
};

这段代码中有几个需要注意的地方:

1.P[i]的计算过程可以不通过分情况考虑,直接用P[i] = min(p[2*id-i],mx-i)来求解,我写成这样是为了更直观的去理解。

2.实际上通过匹配计算P[i],都是mx - i<= p[2*id-i]的情况,只有超过mx的地方才通过匹配计算p[i],所以它的时间是严格的线性的。

3.mx = p[i] +i -1,因为p[i]实际上也把s[i]也考虑进去了,所以此处还要减去1。

4.在最后计算最长回文子串在原字符串中的部分时,起始点为i-p[i]/2,(这也是通过观察的得知的),这一点需要注意。

提交结果:Accepted( 6ms).确实很快...

参考:

http://blog.csdn.net/suool/article/details/38383045

LeetCode——5.Longest Palindromic Substring的更多相关文章

  1. LeetCode(4) || Longest Palindromic Substring 与 Manacher 线性算法

    LeetCode(4) || Longest Palindromic Substring 与 Manacher 线性算法 题记 本文是LeetCode题库的第五题,没想到做这些题的速度会这么慢,工作之 ...

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

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

  3. 求最长回文子串 - leetcode 5. Longest Palindromic Substring

    写在前面:忍不住吐槽几句今天上海的天气,次奥,鞋子里都能养鱼了...裤子也全湿了,衣服也全湿了,关键是这天气还打空调,只能瑟瑟发抖祈祷不要感冒了.... 前后切了一百零几道leetcode的题(sol ...

  4. LeetCode 5 Longest Palindromic Substring(最长子序列)

    题目来源:https://leetcode.com/problems/longest-palindromic-substring/ Given a string S, find the longest ...

  5. 【JAVA、C++】LeetCode 005 Longest Palindromic Substring

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

  6. leetcode:Longest Palindromic Substring(求最大的回文字符串)

    Question:Given a string S, find the longest palindromic substring in S. You may assume that the maxi ...

  7. [LeetCode][Python]Longest Palindromic Substring

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com'https://oj.leetcode.com/problems/longest ...

  8. 【LeetCode】Longest Palindromic Substring 解题报告

    DP.KMP什么的都太高大上了.自己想了个朴素的遍历方法. [题目] Given a string S, find the longest palindromic substring in S. Yo ...

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

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

  10. 最长回文子串-LeetCode 5 Longest Palindromic Substring

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

随机推荐

  1. 【Java】输出目录结构

    import java.io.*; import java.io.File; import java.io.IOException; public class FileUtil { public st ...

  2. 打印GBK、GB2312字符集全字符

    根据编码表填充数据就可以了~~~~(>_<)~~~~~\(≧▽≦)/~啦啦啦 #include <stdio.h> #include <stdlib.h> #inc ...

  3. SEO:网站优化内容

    一.内部优化 (1)meta标签优化:例如:TDK等的优化:        首页:网站名称 或者 网站名称_提供服务介绍or产品介绍 .        频道页:频道名称_网站名称.        文章 ...

  4. Learning to Rank Short Text Pairs with Convolutional Deep Neural Networks(paper)

    本文重点: 和一般形式的文本处理方式一样,并没有特别大的差异,文章的重点在于提出了一个相似度矩阵 计算过程介绍: query和document中的首先通过word embedding处理后获得对应的表 ...

  5. C与C++中实现 gotoxy()函数

    #include <stdio.h> #include <windows.h> void gotoxy(int x, int y) { COORD pos = {x,y}; H ...

  6. gogs打造自己的git

    推荐docker安装 //下载镜像 docker pull gogs/gogs // 创建容器 docker run -d --name=gogs -p 10022:22 -p 3000:3000 - ...

  7. Red Hat OpenShift

    Core Concepts 基本概念 Master Master监控其他Node和Pod 提供Web Console Node Node是k8s的工作机器 Node中运行Pod,pod可以在node中 ...

  8. AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...

  9. Go Example--方法

    package main import "fmt" //定义结构体 type rect struct { width,height int } //定义结构体指针的方法 func ...

  10. LeetCode – Group Shifted Strings

    Given a string, we can "shift" each of its letter to its successive letter, for example: & ...