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)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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,关于后缀数组请关注我的后续博客

本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3675788.html,如需转载请自行联系原作者

LeetCode:Longest Palindromic Substring 最长回文子串相关推荐

  1. LeetCode 5. Longest Palindromic Substring 最长回文子串 Python 四种解法(Manacher 动态规划)

    Longest Palindromic Substring 最长回文子串 学习笔记 1. Brute method 第一种方法:直接循环求解,o(n2)o(n^2) class Solution:de ...

  2. Java Longest Palindromic Substring(最长回文字符串)

    假设一个字符串从左向右写和从右向左写是一样的,这种字符串就叫做palindromic string.如aba,或者abba.本题是这种,给定输入一个字符串.要求输出一个子串,使得子串是最长的padro ...

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

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

  4. 0005-Longest Palindromic Substring(最长回文子串)

    这个系列算是出于个人兴趣开的一个新坑吧,最近看到同学刷LeetCode算法题,就想写写那些可以一行Python代码写出来的题目,因此本专栏的文章的解题方式效率不做保证,只为追求"一行的浪漫& ...

  5. C++longest palindromic subsequence最长回文子序列算法实现(附完整源码)

    C++longest palindromic subsequence最长回文子序列算法 C++longest palindromic subsequence最长回文子序列算法实现完整源码(定义,实现, ...

  6. Leetcode题库 5.最长回文子串(C++实现)

    文章目录 解析 思路 代码 解析 l为字符串长度 Max为最长回文子串长度 Max_R指向最长回文子串头部 Max_L指向最长回文子串尾部 p_r指向当前回文子串头部下标-1 p_l指向当前回文子串尾 ...

  7. 【LeetCode 5-中等】最长回文子串(高清截图)

    5. [中等]最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ 给你一个字符串s,找到s中最长的回文子串. ...

  8. 【LeetCode笔记】5.最长回文子串(Java、动态规划、字符串)

    文章目录 题目描述 解法 & 代码: 思路 题目描述 回文:正着念和倒着念一样. 解法 & 代码: 一开始看到子串,想着可能no.3最长重复子串一样用滑动窗口.不过回文串的判断会很麻烦 ...

  9. LeetCode琅琊榜第二层-最长回文子串问题(动态规划)

    LeetCode_5.最长回文字串 难度:中等 关注博主,持续输出优质算法内容 题目链接 目录 1.暴力求解法

最新文章

  1. 大脑构造图与功能解析_施工技术特辑 | 全套脚手架三维构造图解析
  2. CMD查询Mysql中文乱码的解决方法
  3. SQL语言之DML语言学习(一) 数据插入与修改操作语言
  4. react学习系列之states与props
  5. mysql创建约束时的约束名称,MySQL唯一键约束
  6. 最全的B端产品经理干货知识(3)
  7. python数据结构-图
  8. linux查找有用日志常用技巧
  9. pcl1.8.1+vs2017
  10. Matlab中的magic函数、vander函数、hilb函数、compan函数、pascal函数、zeros函数、ones函数、eye函数、rand函数、randn函数
  11. DirectShow介绍-1
  12. 抖音店群玩法之后端维护
  13. vue中对鼠标划过事件处理方式
  14. 请画出使用mapreduce对英文句子_英文写作中有哪些常见的标点符号问题?小心你的essay会被教授diss不专业...
  15. 压缩或解压文件出现循环冗余检查的解决办法
  16. C++搜索算法和曼哈顿距离之最少连通代价
  17. android vr游戏开发,安卓VR成人游戏合集
  18. php ios表情包,十分钟开发一款 iOS 表情包 App
  19. python人像绘制_CVPR 2019 | APDrawingGAN:人脸秒变艺术肖像画
  20. 在OpenCV里使用图片修复

热门文章

  1. python免费试听-线上儿童编程免费试听-品质保障
  2. python基础教程第三版豆瓣-想学python,请问有没好的书籍推荐?
  3. python在日常工作处理中的应用-谈谈Python在工作中的应用
  4. javascript高级程序设计之BOM
  5. PHP语法分析器:RE2C BISON 总结
  6. LeetCode Mini Parser(栈操作)
  7. nginx的内存池及内存管理
  8. 【helloworld】-微信小程序开发教程-入门篇【1】
  9. 基于MD5的增强型摘要算法
  10. 浅谈错排公式的推导及应用