1. 问题描述:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
2. 思路分析:
① 我们可以使用一个for循环,表示当前字符串的位置,这也表示右指针的位置,使用一个左指针来记录回文串的最左边的位置,首先是判断0到当前的位置是否是回文串,假如是回文串那么看一下当前的回文串的长度是否大于了历史上的回文串的长度,假如大于了那么更新最长回文长度,假如0到当前的位置不是回文的长度,那么对于0到当前的位置的字符串的范围内进行缩小,逐步缩小回文的范围,在0到当前的字符这个范围找到第一个回文串即可,因为找到第一个之后剩下来的字符就不需要看了因为后面的缩小范围之后就更短了,这也是可以优化的一个点
其实思路还是比较简单,先判断最长,假如最长的符合,那么继续下一个字符串的判断,假如不是那么需要在这个范围内找到第一个满足条件的字符串
② 判断当前的字符串是否是回文串,不要使用java中的api,比如像StringBuilder这样,因为这样判断的话会超时,需要进行手动判断,对于首位的字符串依次判断是否相等,不相等那么肯定不是回文串直接返回,这样的话才不会超时,提交代码上去可以通过但是耗时还是比较大的,好像对于数据量大的要1秒多,假如使用api的话时间更长
③ 这个代码即使是做了优化但是耗时还是比较大的,看了一下官方的题解,发现其中的一种中心扩展算法比较好理解,而且耗时比我上面写的要短一点,实现思路还是比较好理解,可以使用debug进行调试的话就更好理解了,它也是在for循环中遍历当前的字符,依次调用一个方法两次,第一次调用是为了检查以当前的字符为中心的字符串是否是回文串,比如像最长的为奇数的回文串abcba,第二次调用方法是为了检查为长度偶数的最长回文串,比如像abba这种字符串,假如经过求解之后得到的更长那么就更新回文串的左端点与右端点,这两个端点可以结合具体的例子来确定值,比如代入abcba或者是abba这种例子
3. 代码如下:
双指针:
import java.util.Scanner;
public class Solution {
public String longestPalindrome(String s) {
if (s.equals("")) return "";
int l = 0, rec = 0;
int res[] = {0 ,0};
for (int r = 0; r < s.length(); ++r){
if (judge(s, 0, r)) {
if (rec < r + 1){
rec = r + 1;
res[0] = 0;
res[1] = r;
}
}
else {
/*优化*/
if (rec > r + 1) continue;
/*注意每一次应该都是从下标为0开始*/
l = 0;
/*子串的范围内寻找是否存在回文串*/
while (l < r){
if (judge(s, l, r) && rec < r - l + 1){
/*找到满足条件的直接break掉因为往右移动肯定是范围在缩小的而这个时候已经找到了
* l-r满足的最大的回文串了
* */
rec = r - l + 1;
res[0] = l;
res[1] = r;
break;
}
++l;
}
}
}
return s.substring(res[0], res[1] + 1);
}
/*判断是否是回文串*/
private boolean judge(String s, int l, int r) {
/*String cur = s.substring(l, i + 1);
StringBuilder sb = new StringBuilder(cur);
return sb.reverse().toString().equals(cur);*/
/*手动检查是否是回文串*/
while (l < r){
if (s.charAt(l) != s.charAt(r)) return false;
++l;
--r;
}
return true;
}
}
官方的中心扩展算法:
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(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;
}
}