leetcode
leetcode 1 ~ 50
最长回文子串

最长回文子串

难度:

标签:

题目描述

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

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

 

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

 

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

代码结果

运行时间: 1000 ms, 内存: 15 MB


/**
 * This solution utilizes Java Stream API to find the longest palindromic substring.
 * However, due to the functional nature of streams, dynamic programming table implementation is less straightforward.
 * We implement a similar solution but in a more functional style where possible.
 */
import java.util.stream.IntStream;
 
public class SolutionStream {
    public String longestPalindrome(String s) {
        int n = s.length();
        if (n <= 1) return s;
 
        boolean[][] dp = new boolean[n][n];
        int[] maxLenStart = {1, 0}; // maxLen, start
 
        IntStream.range(0, n).forEach(i -> dp[i][i] = true);
 
        IntStream.range(0, n - 1).filter(i -> s.charAt(i) == s.charAt(i + 1)).forEach(i -> {
            dp[i][i + 1] = true;
            maxLenStart[0] = 2;
            maxLenStart[1] = i;
        });
 
        IntStream.range(3, n + 1).forEach(len ->
            IntStream.range(0, n - len + 1).forEach(i -> {
                int j = i + len - 1;
                if (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]) {
                    dp[i][j] = true;
                    maxLenStart[0] = len;
                    maxLenStart[1] = i;
                }
            })
        );
 
        return s.substring(maxLenStart[1], maxLenStart[1] + maxLenStart[0]);
    }
}

解释

方法:

这个题解使用了中心扩展法来寻找最长回文子串。对于字符串中的每个位置,以该位置为中心,向两边扩展,找到以该位置为中心的最长回文子串。需要考虑回文子串长度为奇数和偶数两种情况。最后返回所有找到的回文子串中最长的一个。

时间复杂度:

O(n^2)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么中心扩展法是解决这个问题的合适方法,与动态规划或其他字符串搜索算法相比有何优势?
中心扩展法在解决最长回文子串问题时非常高效,因为它直接利用了回文的对称性质。与动态规划相比,中心扩展法的空间复杂度较低,为 O(1),因为它不需要额外的存储空间来保存子问题的解。此外,中心扩展法的时间复杂度通常优于动态规划,尤其是在最坏情况下,动态规划需要 O(n^2) 的时间和空间复杂度。而中心扩展法的时间复杂度为 O(n^2),但在实际应用中常常表现出更好的性能,因为并不是所有的中心都需要扩展到最大可能的长度。
🦆
在扩展过程中,如果字符序列长度为偶数,如何选择中心点来确保不遗漏任何可能的回文子串?
在处理偶数长度的回文子串时,中心扩展法需要考虑两个连续字符作为中心点。具体来说,如果考虑索引 i 和 i+1 作为中心,这两个索引之间实际上没有字符,可以视为在两个字符之间有一个虚拟的中心。从这种虚拟的中心开始扩展,可以确保找到所有可能的偶数长度的回文子串。这种处理方式确保了不会遗漏任何偶数长度的回文子串,同时也很自然地融入了中心扩展法的框架中。
🦆
扩展函数在检测到不匹配字符时立即停止,这是否意味着总是返回正确的回文长度,还是可能存在需要调整的边界情况?
扩展函数在检测到不匹配的字符时立即停止确实可能存在边界调整的需求,但这通常不影响回文长度的正确性。函数在检测到不匹配时停止,而返回的子串是从 l+1 到 r-1(不包括 r),这恰好是最后一个匹配的有效回文子串。因此,尽管在实际实现中需要正确处理边界(例如,确保不越界),扩展停止的位置是正确的,无需额外调整。
🦆
为什么在寻找最长回文子串时需要单独处理奇数和偶数长度的情况,不能统一处理?
奇数和偶数长度的回文子串具有不同的对称性质。奇数长度的回文中心是一个实际的字符,而偶数长度的回文中心是两个字符之间的虚拟位置。这种结构上的差异导致在寻找回文时必须分别处理。如果不区分处理,就无法准确地从字符串中心扩展出所有可能的回文子串,因为奇偶长度的中心点选取方式不同。因此,为了全面覆盖所有回文可能性,单独处理奇数和偶数长度的情况是必要的。

相关问题

最短回文串

给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。

 

示例 1:

输入:s = "aacecaaa"
输出:"aaacecaaa"

示例 2:

输入:s = "abcd"
输出:"dcbabcd"

 

提示:

  • 0 <= s.length <= 5 * 104
  • s 仅由小写英文字母组成

回文排列

回文排列

回文对

给定一个由唯一字符串构成的 0 索引 数组 words 。

回文对 是一对整数 (i, j) ,满足以下条件:

  • 0 <= i, j < words.length
  • i != j ,并且
  • words[i] + words[j](两个字符串的连接)是一个回文串

返回一个数组,它包含 words 中所有满足 回文对 条件的字符串。

你必须设计一个时间复杂度为 O(sum of words[i].length) 的算法。

 

示例 1:

输入:words = ["abcd","dcba","lls","s","sssll"]
输出:[[0,1],[1,0],[3,2],[2,4]] 
解释:可拼接成的回文串为 ["dcbaabcd","abcddcba","slls","llssssll"]

示例 2:

输入:words = ["bat","tab","cat"]
输出:[[0,1],[1,0]] 
解释:可拼接成的回文串为 ["battab","tabbat"]

示例 3:

输入:words = ["a",""]
输出:[[0,1],[1,0]]
 

提示:

  • 1 <= words.length <= 5000
  • 0 <= words[i].length <= 300
  • words[i] 由小写英文字母组成

最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

 

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

示例 2:

输入:s = "cbbd"
输出:2
解释:一个可能的最长回文子序列为 "bb" 。

 

提示:

  • 1 <= s.length <= 1000
  • s 仅由小写英文字母组成

回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

 

示例 1:

输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

 

提示:

  • 1 <= s.length <= 1000
  • s 由小写英文字母组成