问题描述
5.最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
题解
1.暴力破解
枚举所有子串,判断是否为回文串,因为题目要求找到最长回文子串,所以从最长子串开始遍历,直到找到一个回文串就一定满足题意,直接结束即可,代码如下:
class Solution {
public String longestPalindrome(String s) {
//i表示子串长度,j表示子串起始位置
for (int i = s.length(); i > 0; i--) {
for (int j = 0; j + i <= s.length(); j++) {
String str = s.substring(j, j + i);
if (str.equals(reverse(str, str.length() - 1))) {
return str;
}
}
}
return "";
}
//反转字符串
private String reverse(String str, int n) {
if (n == 0) {
return str.charAt(n)+"";
}
return str.charAt(n) + reverse(str, n - 1);
}
}
这种方法显然完全不可取,对于很长的字符串处理的非常慢
2.动态规划
定义dp[i][j]表示字符串中第i个字符到第j个字符组成的字符串( 即s[i…j] )是否为回文子串,是则为true,否则为false.
初始化
由于单个字符一定是回文子串,所以所有的dp[i][i]都为true
定义dp方程
根据头尾相等可以得出dp[i][j]就相当于s[i]==s[j]和dp[i+1][j-1]的交集,即
代码如下
class Solution {
public String longestPalindrome(String s) {
if (s.length() == 0) {
return "";
}
int len = s.length();
boolean[][] dp = new boolean[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
int maxLen = 1;
int start = 0;
for (int subLen = 2; subLen <= len; subLen++) {
for (int i = 0; i + subLen - 1 < len; i++) {
int j = i + subLen - 1;
if (i + 1 > j - 1) {
dp[i][j] = s.charAt(i) == s.charAt(j);
} else {
dp[i][j] = s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1];
}
if (dp[i][j] && subLen > maxLen) {
maxLen = subLen;
start = i;
}
}
}
return s.substring(start, start + maxLen);
}
}
动态规划本质上跟暴力破解差不多,但实际运行起来比暴力破解要快不少。
3.中心扩散
我们知道,回文串都是中心对称的,只需要遍历所有字符作为中心,从中心向两边扩散出去,看看能向两边扩散多远
需要注意的是这里分两种情况,奇数回文串和偶数回文串的中心是不一样的
- 奇数回文串的中心是单个字符,例如"aba"中心是"b"。
- 偶数回文串的中心是两个字符,例如"abba"中心是"bb"。
代码如下:
class Solution {
public String longestPalindrome(String s) {
if (s.length() == 0) {
return "";
}
int maxLen = 1;
int start = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = longestPalindrome(s, i, i);
int len2 = longestPalindrome(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > maxLen) {
maxLen = len;
start = i - (len - 1) / 2;
}
}
return s.substring(start, start + maxLen);
}
private int longestPalindrome(String s, int left, int right) {
int l = left, r = right;
while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
l--;
r++;
}
return r - l - 1;
}
}