题目介绍
力扣5题:leetcode-cn.com/problems/lo…
方法一:动态规划
对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串 “ababa”,如果我们已经知道 “bab” 是回文串,那么 “ababa” 一定是回文串,这是因为它的首尾两个字母都是 “a”。
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P(i,j) 表示字符串 s 的第 i 到 j 个字母组成的串。
那么我们就可以写出动态规划的状态转移方程:
代码如下:
public String longestPalindrome(String s) {
if (s == null || s.length() < 2) {
return s;
}
int strLen = s.length();
int maxStart = 0; //最长回文串的起点
int maxEnd = 0; //最长回文串的终点
int maxLen = 1; //最长回文串的长度
boolean[][] dp = new boolean[strLen][strLen];
for (int r = 1; r < strLen; r++) {
for (int l = 0; l < r; l++) {
//r - l <= 2 表示两者之间只有一个字符
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > maxLen) {
//更新最大值
maxLen = r - l + 1;
maxStart = l;
maxEnd = r;
}
}
}
}
return s.substring(maxStart, maxEnd + 1);
}
方法二:中心扩散法
对于这个问题,我们首先应该思考的是,给一个字符串s
,如何在s
中找到一个回文子串?
有一个很有趣的思路:既然回文串是一个正着反着读都一样的字符串,那么如果我们把s
反转,称为s'
,然后在s
和s'
中寻找最长公共子串,这样应该就能找到最长回文子串。
比如说字符串abacd
,反过来是dcaba
,它俩的最长公共子串是aba
,也就是最长回文子串。
但是这个思路是错误的,比如说字符串aacxycaa
,反转之后是aacyxcaa
,最长公共子串是aac
,但是最长回文子串应该是aa
。
虽然这个思路不正确,但是这种把问题转化为其他形式的思考方式是非常值得提倡的。
下面,就来说一下正确的思路,如何使用双指针。
寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串。对于最长回文子串,就是这个意思:
for 0 <= i < len(s):
找到以 s[i] 为中心的回文串
更新答案
但是呢,我们刚才也说了,回文串的长度可能是奇数也可能是偶数,如果是abba
这种情况,没有一个中心字符,上面的算法就没辙了。所以我们可以修改一下:
for 0 <= i < len(s):
找到以 s[i] 为中心的回文串
找到以 s[i] 和 s[i+1] 为中心的回文串
更新答案
PS:读者可能发现这里的索引会越界,等会会处理。
按照上面的思路,先要实现一个函数来寻找最长回文串,这个函数是有点技巧的:
public String palindrome(String s ,int l ,int r) {
//防止越界
while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
//向两边扩展
l--;
r++;
}
return s.substring(l + 1, r);
}
为什么要传入两个指针l
和r
呢?因为这样实现可以同时处理回文串长度为奇数和偶数的情况:
for 0 <= i < len(s):
# 找到以 s[i] 为中心的回文串
palindrome(s, i, i)
# 找到以 s[i] 和 s[i+1] 为中心的回文串
palindrome(s, i, i + 1)
更新答案
完整代码如下:
class Solution {
public String longestPalindrome(String s) {
String result = "";
for(int i = 0 ; i < s.length() ; i++) {
String s1 = palindrome(s, i,i);
String s2 = palindrome(s, i,i + 1);
//更新最大值
result = result.length() > s1.length() ? result : s1;
//更新最大值
result = result.length() > s2.length() ? result : s2;
}
return result;
}
/**
* 中心扩展法
*/
public String palindrome(String s ,int l ,int r) {
//防止越界
while(l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
//向两边扩展
l--;
r++;
}
return s.substring(l + 1, r);
}
}
至此,这道最长回文子串的问题就解决了,时间复杂度 O(N^2),空间复杂度 O(1)。
值得一提的是,这个问题可以用动态规划方法解决,时间复杂度一样,但是空间复杂度至少要 O(N^2) 来存储 DP table。这道题是少有的动态规划非最优解法的问题。