leetcode_【5】最长回文子串

1.题目描述

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

示例 2:

输入: “cbbd”
输出: “bb”

2.解题思路

方法1:暴力破解方法
方法2:动态规划

新建一个dp[i][j]代表的意思是索引从i到j的子字符串是否是回文,假设s = cbbd,则可以dp对应坐标索引下的子字符串:

i = 0 1 2 3
j = 0 c cb cbb cbbd
1 b bb bbd
2 b bd
3 d

如果dp[i][j]所代表的字符串为回文,则将dp设为1.由上表可以总结出:

(1)当i = j 时,dp[j][i] = 1

(2)i-j=1时,比如dp[1][2]为bb,表示两个相邻的字符,此时我们只要判断str[1]==str[2]就能得出dp[1][2]的结果

(3)i-j>1时,我们来看dp[0][2],首先还是要判断开头和结尾是否相等,也就是判断 str[0]==str[2],假如此时str[0]=str[2],我们还要再看剩下的子串是否回文, 我们可以直接从dp[j+1][i-1]来判断剩下的子串,把结果直接拿来用,判断是否是1(1表示回文)

即有公式

dp[i][j] = (dp[i+1][j-1] &&s[i]=S[j]​) == true?1:0

dp数组初始化如下:

dp[i][i] = 1

dp[i][i+1] = ( S[i] == S[i+1] ?1:0;

方法3:中心扩展方法

回文中心的两侧互为镜像。因此,回文可以从他的中心展开,并且只有2n-1个这样的中心(一个元素为中心的情况有n个,两个元素为中心的情况有n-1个)

方法4:manacher算法

(1)预处理:回文的长度可奇可偶,故在每个字符的左右都加上一个特殊字符“#”,得到长度为奇数的字符串

(2)回文子串的半径:以中间的 ‘1’ 为中心的回文子串 “#2#2#1#2#2#” 的半径是6,而未添加#号的回文子串为 “22122”,长度是5,为半径减1。

(3)子串的起始位置:(字符串前面在加一个特殊字符”\$”,在字符末尾加另一个特殊字符”.”)中间位置减去半径再除以2。

(4)子串的终点位置:起点位置+半径-1

(5)马拉车核心

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

其中p[i]代表以i为中心的半径,id为能延伸到最右端的位置的那个回文子串的中心点位置,mx是回文串能延伸到的最右端的位置

manecher

1)mx - i > p[j],即以j为半径的回文在以id为半径的回文内(j跟i是对称的),其中 j = 2*id - i,因为 j 到 id 之间到距离等于 id 到 i 之间到距离,为 i - id,所以 j = id - (i - id) = 2*id - i.

manecher

2)mx - i < p[j],即以j为中心的回文子串不一定完全包含于以id为中心的回文子串中,基于对称性可知,图中两个绿框所包围的部分是相同的,也就是说以i为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 p[i] = mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了,这就是后面紧跟到while循环的作用。

3)对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。

3.代码

思路1:暴力破解方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static String longestPalindrome(String s) {
String temp = "";
String res ="";
for(int i = 0;i<s.length();i++){
for(int j = i;j<s.length();j++){
temp = temp+s.charAt(j);
//re用来保存子字符串反转的结果
String re = new StringBuffer(temp).reverse().toString();
//子字符串跟反转的字符串相等则为回文
if(temp.equals(re)){
res = res.length()>temp.length()?res:temp;
}
}
temp = "";
}
return res;
}

方法2:动态规划思想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if(s == null || len == 0){
return s;
}
String res = "";
int max = 0;
//创建一个行列均为字符串长度的二维数组,创建时默认初始化为false
boolean[][] dp = new boolean[len][len];
for(int j = 0; j < len; j++){
for(int i = 0; i <= j; i++){
dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1]);
if(dp[i][j]){//如果是回文字符串
//新得到的回文比之前的回文字符串要长,更新字符串长度,记录字符串
if(j - i + 1 > max){
max = j - i + 1;
res = s.substring(i, j + 1);
}
}
}
}
return res;
}
}

方法3:中心扩展方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;
}
}

方法4:马拉车算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution { 
public String longestPalindrome(String s) {
if (s == null || s.length() < 1)
return "";
//manacher预处理
//起点加特殊字符$
String t = "$#";
//每个字符左右都加特殊字符 #
for (int i = 0; i < s.length(); ++i) {
t += s.charAt(i);
t += "#";
}
//终点加特殊字符&
t+="&";
//马拉车算法实现
//定义半径
int []p = new int[t.length()];
int mx = 0, id = 0, resLen = 0, resCenter = 0;
for (int i = 1; i < t.length()-1; i++) {
//mancher核心算法
p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
//半径往外扩
while (t.charAt(i + p[i]) == t.charAt(i-p[i]))
++p[i];
//以i为中心的回文子串不一定完全包含于以id为中心的回文子串中
if ((mx-i) < p[i]) {
mx = i + p[i];
id = i;
}
if (resLen < p[i]) {
resLen = p[i];
resCenter = i;
}
}
return s.substring((resCenter - resLen) / 2, (resCenter - resLen) / 2 + resLen - 1);
}
}

4.提交记录

动态规划

中心扩展

马拉车算法

文章目录
  1. 1. 1.题目描述
  2. 2. 2.解题思路
    1. 2.0.1. 方法1:暴力破解方法
    2. 2.0.2. 方法2:动态规划
    3. 2.0.3. 方法3:中心扩展方法
    4. 2.0.4. 方法4:manacher算法
  • 3. 3.代码
  • 4. 4.提交记录
  • | 139.6k