题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
思路
- 在解决一道算法问题时,首先不能忽视暴力解法,因为暴力解法是它最根本的计算逻辑。
- 在暴力解法的基础上利用时间换空间或者空间换时间等解法进行优化
java
暴力法——时间复杂度O(n^2),双层循环再加初始化变量开销,超时
class Solution {
public String longestPalindrome(String s) {
int max = 0;
String res = "";
int n = s.length();
for(int i = 0; i < n; i++) {
for(int j = i; j < n; ++j ) {
// substring(i,j) 是左闭右开
String str = s.substring(i,j+1);
StringBuilder sb = new StringBuilder(str);
// reverse() 针对IO流,不针对字符串,要使用StringBuilder
if(str.equals(sb.reverse().toString()) && str.length() > max) {
res = str;
max = str.length();
}
}
}
return res;
}
}
中心扩张
——时间复杂度O(n^2):双层循环,每层循环最大为n,想乘
——空间复杂度O(1):每次只保存当前在计算的字符串
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
if(n < 2) {
return s;
}
String res = "";
String str = "";
for(int i = 0; i < n - 1; i++) {
str = findPalindrome(s, i, i);
if(str.length() > res.length()) {
res = str;
}
str = findPalindrome(s, i, i+1);
if(str.length() > res.length()) {
res = str;
}
}
return res;
}
public String findPalindrome(String s, int left, int right) {
int n = s.length();
while(left >= 0 && right <= n - 1 && (s.charAt(left) == s.charAt(right))) {
left--;
right++;
}
return s.substring(left + 1, right);
}
}
马拉车算法 Manacher Algorithm(头疼,保存下来慢慢看吧)
时间 O(n) 空间 O(n)
class Solution {
public String longestPalindrome(String s) {
if (s.length() <= 1) {
return s;
}
// 预处理字符串,避免奇偶问题
String str = preProcess(s);
// idx是当前能够向右延伸的最远的回文串中心点,随着迭代而更新
// max是当前最长回文串在总字符串中所能延伸到的最右端的位置
// maxIdx是当前已知的最长回文串中心点
// maxSpan是当前已知的最长回文串向左或向右能延伸的长度
int idx = 0, max = 0;
int maxIdx = 0;
int maxSpan = 0;
int[] p = new int[str.length()];
for (int curr = 1; curr < str.length(); curr++) {
// 找出当前下标相对于idx的对称点
int symmetryOfCurr = 2 * idx - curr;
// 如果当前已知延伸的最右端大于当前下标,我们可以用对称点的P值,否则记为1等待检查
p[curr] = max > curr ? Math.min(p[symmetryOfCurr], max - curr) : 1;
// 检查并更新当前下标为中心的回文串最远延伸的长度
while ((curr + p[curr]) < str.length() && str.charAt(curr + p[curr]) == str.charAt(curr - p[curr])) {
p[curr]++;
}
// 检查并更新当前已知能够延伸最远的回文串信息
if (curr + p[curr] > max) {
max = p[curr] + curr;
idx = curr;
}
// 检查并更新当前已知的最长回文串信息
if (p[curr] > maxSpan) {
maxSpan = p[curr];
maxIdx = curr;
}
}
//去除占位符
return s.substring((maxIdx - maxSpan) / 2, (maxSpan + maxIdx) / 2 - 1);
}
private String preProcess(String s) {
// 如ABC,变为$#A#B#C#
StringBuilder sb = new StringBuilder();
sb.append("$");
for (int i = 0; i < s.length(); i++) {
sb.append("#");
sb.append(s.charAt(i));
}
sb.append("#");
return sb.toString();
}
}