Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >多种思路深度剖析经典面试题---最长回文子串

多种思路深度剖析经典面试题---最长回文子串

原创
作者头像
程序员小熊
修改于 2021-05-17 03:12:48
修改于 2021-05-17 03:12:48
66400
代码可运行
举报
运行总次数:0
代码可运行

前言

大家好,我是程序员小熊,来自大厂的程序猿。最长回文子串是面试中常考的题目,尤其是一些互联网大厂,像亚马逊、微软、脸书、字节和腾讯等考过这道题。

本文提供四种解题思路三种解法,将本题解法的时间复杂度O(n^3) 一步步降为 O(n) ,供大家参考,希望对大家有所帮助。

题目

给你一个字符串 s,找到 s 中最长的回文子串。

示例
示例

解题思路

本题要求的是最长回文子串,必须先明确两个概念。

回文串从左向右读跟从右向左读都一样的字符串。例如 “level”或者“noon”等等。

子串:原始字符串中任意个连续的字符组成的子序列。

由于题目提示 1 <= s.length <= 1000,因此设计一个 O(n^2) 的算法是合理的。因为 O(n^2) 的算法可以在 1s 内处理大约 10^4 级别的数据;并且从示例1中可以知道,如果字符串存在多个最长回文子串,只需要输出一个即可。

暴力法

以字符串 "abba" 为例子,如下动图所示。

暴力法动图
暴力法动图

要求字符串的最长回文子串,只需要先找出最长回文子串的起始位置,再求出最长回文子串的长度即可。

因此可以通过两层遍历枚举长度大于等于2且超过当前得到的最长回文子串长度的子串,再加一层判断子串是否是回文串的遍历,就可求出给定字符串的最长回文子串。

Show me the Code

java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* 验证子串 s[left...right]是否为回文子串 */
boolean isPalindrome(char[] charArray, int left, int right) {
    while (left < right) {
        if (charArray[left] != charArray[right]) {
            return false;
        }
        left++;
        right--;
    }

    return true;
} 

String longestPalindrome(String s) {
    int len = s.length();
    /* 长度少于 2 的字符串的最长回文子串是其自身 */
    if (len < 2) {
        return s;
    }

    int maxLen = 1;   // 记录最长回文子串的长度
    int start = 0;    // 记录最长回文子串的起始位置
    char[] charArray = s.toCharArray();
    for (int i = 0; i < len - 1; i++) {
        for (int j = i + 1; j < len; j++) {
            /* 回文子串 s[i...j] 的长度超过当前最长回文子串长度 */
            if (j - i + 1 > maxLen && isPalindrome(charArray, i, j)) {
                /* 更新最长回文子串的长度 */
                start = i;
                maxLen = j - i + 1;
            }
        }
    }

    return s.substring(start, start + maxLen);
}

复杂度分析

时间复杂度:三层遍历 O(n^3)

空间复杂度:没有开辟额外空间 O(1)

动态规划

回文串具有天然状态转移性,一个长度大于 2 的回文串,去掉首尾两头之后,剩余的部分依然是回文串。

情况一:如果一个子串首尾两头的字符不相同,则该子串不是回文串。如下图示。

回文串判断1
回文串判断1

情况二:如果一个子串首尾两头的字符相同,则去掉首尾两头的字符,继续判断去掉后的子串,直至子串的首尾两头的字符不相同或子串为空。如下动图示。

回文串判断2
回文串判断2

也就是说一个子串首尾两头的字符相同去掉首尾两头的字符后剩余的子串是否是回文串决定原子串是否是回文串

状态:dp[i][j] 表示子串 s[i...j] 是否为回文子串。

状态转移方程

状态转移方程
状态转移方程

边界条件:[i + 1...j - 1]不成立(构成区间)

边界条件
边界条件

整理得:

边界条件
边界条件

即当 len(s[i...j])= 2 or 3 时,不用检查子串是否是回文串,不需要状态转移。

初始化:单个字符一定是字符串 dp[i][j] = true。

输出:状态为 true 时,记录当前最长回文子串的起始位置和长度,填完表后截取字符串。

举例 以字符串 "abcba" 为例子,如下动图示,表格中的 dp[i][j] 也可参考它左下方的值填写。

状态转移表
状态转移表

Show me the Code

java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String longestPalindrome(String s) {
    int len = s.length();
    if (len < 2) {
        return s;
    }

    int start = 0;
    int maxLen = 1;

    /* dp[i][j]:s[i...j] 是否是回文串 */
    boolean[][] dp = new boolean[len][len];
    for (int i = 0; i < len; i++) {
        dp[i][i] = true;
    }

    char[] charArray = s.toCharArray();
    for (int j = 1; j < len; j++) {
        for (int i = 0; i < j; i++) {
            if (charArray[i] != charArray[j]) {
                dp[i][j] = false;
            } else {
                if (j - i < 3) {
                    dp[i][j] = true;
                } else {
                    dp[i][j] = dp[i + 1][j - 1];
                }
            }

            /* s[i...j] 是回文串,记录当前最长回文串长度和起始位置 */
            if (dp[i][j] && j - i + 1 > maxLen) {
                maxLen = j - i + 1;
                start = i;
            }
        }
    }

    return s.substring(start, start + maxLen);
} 

相较于暴力法动态规划利用状态转移方程,快速判断一个子串是否是回文串,每一步计算都尽可能地利用之前计算的结果(保存的),空间换时间

复杂度分析

时间复杂度O(n^2)

空间复杂度O(n^2)

中心扩散法

回文串的枚举可以从两边开始,也可以从中间开始,所以可以枚举所有可能的回文子串的中心位置

由于奇/偶数长度的回文子串的中心位置不一样,所以枚举时需要考虑回文子串长度是奇数还是偶数,如下图示。

偶数回文串
偶数回文串
奇数回文串
奇数回文串

举例

以奇数长度的字符串 "abcba" 为例子,中心扩散如下动图示,

中心扩散动图
中心扩散动图

Show me the Code

C++

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* s[left] 和 s[right] 想两断扩散, 求以 s[left] 和 s[right] 为中心的最长回文串*/
string longestPalindromeHelper(string s, int left, int right) { 
    while (left >= 0 && right < s.length() && s[left] == s[right]) {
        left--;
        right++;
    }
    
    return s.substr(left + 1, right - left - 1);
}

string longestPalindrome(string s) {
    int len = s.length();
    if (len < 2) {
        return s;
    }

    string res;
    for (int i = 0; i < len; i++) {
        /* 以s[i]作奇数回文子串的的回文中心向两边扩散,得到的最长回文子串*/
        string s1 = longestPalindromeHelper(s, i, i);
        /* 以s[i]、s[i + 1] 分别作偶数回文子串的的回文中心向两边扩散,得到的最长回文子串*/
        string s2 = longestPalindromeHelper(s, i, i + 1);
        res = res.size() > s1.size() ? res : s1;
        res = res.size() > s2.size() ? res : s2;
    }

    return res;
}

复杂度分析

时间复杂度O(n^2),枚举中心位置的个数是 2(n - 1),每次向两边扩散检测试服是回文子串。

空间复杂度O(1)

Manacher 算法

通过将原始字符串进行预处理(用不在输入字符串中的字符隔开),使得奇/偶数长度的回文串的性质统一表示。该算法专门用于查找最长回文子串,其时间复杂度为 O(n)

由于绝大多数笔/面试不要求掌握次算法,所以这里就不再介绍了,感兴趣的童鞋,可以查阅相关资料进一步了解。

往期字符串相关精彩文章

动态规划经典题之最长上升子序列最终版

动态规划经典题之最长上升子序列

字符串最长子串难?滑动窗口拯救你

更多精彩

关注公众号【程序员小熊

微信公众号
微信公众号

后台回复【算法】,即可获取高清无码的经典算法电子书~

后台回复【python】,即可获取高清无码的经典 python 电子书~

后台回复【1024】,即可获取三份来自 BAT 大佬的 Leetcode 刷题手册~

为了回馈读者,本公众号不定期会有送礼活动,敬请关注~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
编辑精选文章
换一批
动态规划算法求最长回文子串
回文串就是正着读和反着读一样的字符串,如“abba”,”abcba”,最长回文子串是字符串的子串中最长的属于回文串的子串。如字符串”abbaabccba”的最长回文子串为”abccba”,本文采用动态规划算法来查找最长回文子串,算法时间复杂度为O(n²)。设状态dp[j][i]表示索引j到索引i的子串是否是回文串。则易得转移方程如下:
全栈程序员站长
2022/06/30
2400
动态规划算法求最长回文子串
搞定大厂算法面试之leetcode精讲20.字符串
方法1.截取字符串,循环字符串,遇到#就截掉最后一个字符,循环完毕之后,最后比较两个去除掉#退格之后的字符串是否相等,时间复杂度O(m+n),m、n是两个字符串的长度。空间复杂度O(1)
全栈潇晨
2021/12/04
7190
多种思路秒杀经典面试题最长回文子串
大家好,我是程序员小熊,来自大厂的程序猿。最长回文子串是面试中常考的题目,尤其是一些互联网大厂,像亚马逊、微软、脸书、字节和腾讯等都考过这道题。
程序员小熊
2021/05/28
6440
多种思路秒杀经典面试题最长回文子串
算法修炼之筑基篇——筑基二层初期(解决最长回文子串问题,马拉车(manacher)算法模板)
通过填充动态规划表格 dp,可以找到最长回文子串的长度和起始位置。该方法的时间复杂度为 O(n^2)。
命运之光
2024/03/20
2660
算法修炼之筑基篇——筑基二层初期(解决最长回文子串问题,马拉车(manacher)算法模板)
经典面试题:最长回文子串
回文串是面试常常遇到的问题(虽然问题本身没啥意义),本文就告诉你回文串问题的核心思想是什么。
帅地
2019/10/08
4060
经典面试题:最长回文子串
最长回文子串(动态规划)
酒楼
2023/12/30
1540
[M枚举] lc5. 最长回文子串(枚举+中心拓展+区间dp)「建议收藏」
回文串一共有两种,即长度为奇数的回文串,长度为偶数的回文串。我们可以枚举回文串的中心(偶数长度回文串假想一个中心就行了),然后分别拿两个指针 l = i - 1,r = i + 1 向左右两边同时拓展,若 s[l]=s[r] 则,l --, r ++。一直进行该操作,直到不等或一方到达边界位置。
全栈程序员站长
2022/09/15
2640
Leetcode算法系列| 5. 最长回文子串
游戏开发小Y
2024/01/18
1740
Leetcode算法系列| 5. 最长回文子串
LeetCode(4-寻找两个正序数组的中位数&&5-最长回文子串&&6-Z形变换)
首先我们需要明白一点,那就是重新组合后的数组长度为奇数或者偶数的时候,中位数的计算方式是不一样的.
萌萌哒的瓤瓤
2022/01/06
2100
LeetCode(4-寻找两个正序数组的中位数&&5-最长回文子串&&6-Z形变换)
LeetCode-5.最长回文子串 中心扩散法
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
扎克蕉
2020/07/21
7610
LeetCode-5-最长回文字串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
benym
2022/07/14
1850
[c/c++]——最长回文子串「建议收藏」
已经很久没有更新关于leetcode的题解了,一个是觉得太费时间,二一个目前网上也有很全面的解答,但是在写leetcode的最长回文子串时,发现很多同学的代码都很长(实际上几行就可以解决的事情),并且c++解答的代码不够完整。最关键的是在一种“马拉车”的算法卡了很久很久,今天把几种求解的方法全部都整理出来,方便大家也便于自己以后复习。
全栈程序员站长
2022/08/15
4120
[c/c++]——最长回文子串「建议收藏」
005. 最长回文子串 | Leetcode题解
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
苏南
2020/12/16
4650
005. 最长回文子串 | Leetcode题解
四种方法求最长回文子串
所谓回文串,就是正着读和倒着读结果都一样的回文字符串。 比如: a, aba, abccba都是回文串, ab, abb, abca都不是回文串。
全栈程序员站长
2022/09/06
1K0
四种方法求最长回文子串
如何求最长回文子串
回文字符串,就是像“12321”这种轴对称形式的字符串。 但并不是所有的字符串都是这种整个串都是回文串的,比如1232。有些计算机问题就是在一个字符串中找出一段最长的回文字符子串,这个时候时候,我们就需要一些算法来求出结构。
全栈程序员站长
2022/08/24
3650
如何求最长回文子串
最长回文子串——马拉车算法详解
马拉车算法(Manacher‘s Algorithm)是用来解决求取一个字符串的最长回文子串问题的。此算法充分利用了回文字符串的性质,将算法复杂度降到了线性,非常值得一学。
全栈程序员站长
2022/07/01
8680
最长回文子串——马拉车算法详解
最长回文子串——马拉车算法
针对最长回文子串相关的问题,马拉车算法应该是比较通用的解法,今天我们就来具体看看这个算法。
健程之道
2020/03/11
8030
最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。啥是回文串?就是字符可以看成是对称的,从左往右读和从右往左读是一样意思,比如:上海自来水来自海上。来看下下面的示例:
用户4456933
2021/06/01
6810
Go算法实战 - 5.【最长回文子串LeetCode-5】
原题链接 https://leetcode-cn.com/problems/longest-palindromic-substring/
junedayday
2021/08/05
3430
【Python 千题 —— 算法篇】寻找最长回文子串
回文串是指一个字符串从左到右和从右到左读都是一样的。寻找一个字符串中的最长回文子串是许多经典算法问题之一,广泛应用于文本处理、数据分析和计算生物学等领域。
繁依Fanyi
2024/09/09
3610
【Python 千题 —— 算法篇】寻找最长回文子串
推荐阅读
相关推荐
动态规划算法求最长回文子串
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验