题目:
给你一个字符串 s
,找到 s
中最长的 回文子串。
示例:
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
分析:
题目要求我们找到一个字符串 s
中的最长回文子串。这里的“回文子串”指的是一个字符串,指它正序和逆序相同。例如,字符串 "babad" 中的 "bab" 和 "aba" 都是回文子串。
实现:
方法一:暴力求解
使用辅助函数:采用双指针,分别指向字符串头尾,若头尾相同则左边加1,右边减1。若一直相等则返回true表明这一截字符串是回文的,并记录左右索引值;反之不是,则继续利用循环更新字符串范围。
class Solution {
// 暴力求解
public String longestPalindrome(String s) {
// 存储最长字符串的索引
int ansl = 0, ansr = 0;
for (int i =0;i<s.length();i++){
for (int j=i;j<s.length();j++){
// 判断字符是否是回文
if (isPalindrome(s,i,j)){
// 更新最长字串
if(j-i>ansr-ansl){
ansl=i;
ansr=j;
}
}
}
}
return s.substring(ansl,ansr+1);
}
// 辅助函数,用来检查字符串s中从索引begin-end的字串是否为回文
boolean isPalindrome(String s, int start, int end){
while (start<=end){
//如果两端字符不相等则不是回文
if(s.charAt(start) != s.charAt(end)){
return false;
}
start++;
end--;
}
return true;
}
}
方法二:动态规划
- 反转字符串
- 使用二维数组去初始化二维数组,其中
f[i][j]
表示字符串s
的前i+1
个字符和rev
的前j+1
个字符的最长公共子串的长度 - 判断,如果
rev.charAt(j)
与s
的第一个字符相同,则f[0][j]
为1。 - 双重循环,从
i=1
开始,遍历s
的每个字符,对于每个字符,再遍历rev
的每个字符,更新f[i][j]
的值。如果s.charAt(i)
和rev.charAt(j)
相同,则f[i][j]
等于f[i-1][j-1] + 1
,表示当前字符匹配,最长公共子串长度加1。 - 更新最长回文子串
class Solution {
public String longestPalindrome(String s) {
// 反转字符串
String rev = new StringBuffer(s).reverse().toString();
int n = s.length();
// 动态规划数组,f[i][j]存储s和rev的最长公共字串长度
int[][] f = new int[n][n];
// 记录最长字串的长度和起始位置
int maxLen = 1;
int begPos = 0;
// 初始化第一列
for (int j = 0; j < n; j++) {
if (rev.charAt(j) == s.charAt(0))
f[0][j] = 1;
}
// 动态规划填表
for (int i = 1; i < n; i++) {
// 初始化第一行
f[i][0] = s.charAt(i) == rev.charAt(0) ? 1 : 0;
for (int j = 1; j < n; j++) {
// 如果字符匹配,则在之前的基础上加1
if (s.charAt(i) == rev.charAt(j))
f[i][j] = f[i - 1][j - 1] + 1;
// 更新最长回文字串的信息
if (f[i][j] > maxLen) {
int befPos = n - j - 1;
if (befPos + f[i][j] - 1 == i) {
maxLen = f[i][j];
begPos = befPos;
}
}
}
}
return s.substring(begPos, begPos + maxLen);
}
}
方法三:中心扩展法
中心扩展法是一种更高效的算法,它利用了回文串的对称性质,通过从每个可能的中心点向两边扩展来寻找最长的回文子串。
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){
while (left>=0 && right<s.length() && s.charAt(left) == s.charAt(right)){
// 向左扩展
left--;
// 向右扩展
right++;
}
// 返回扩展后的回文长度
return right - left - 1;
}
}