Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

Longest Palindromic Substring

题目

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

1
2
3
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

1
2
输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

我的解答

思路,两次循环直接遍历所有可能性。O(n^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
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
public String longestPalindrome(String s) {

if(s.length() == 1){
return s;
}

int n = s.length();

String result = "";
for (int i = 0 ; i < n; i++){
for (int j= i+1 ; j<=n ; j++){
// 小长度的直接跳过
int length = result.length();
if(length >= j-i){
continue;
}
String sub = s.substring(i,j);

if(isPalindrome(sub) ){
result = sub;
}
}
}
return result;
}

private boolean isPalindrome(String s){
int length = s.length();
int mid = length/2;
for (int i = 0 ;i< mid; i++){
if(s.charAt(i) != s.charAt(length-i-1)){
return false;
}
}
return true;
}
}

官方参考

第二种解答方法,中心扩散法。
因为所有的相同字符串都算是回文,只要找到两边第一个与中心不相等的位置,相互判断是否相等来判断是否是回文。
遍历每一个元素,以其为中心向两边扩散判断是否回文即可。

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
38
39
40
41
42
43
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return "";
}
if ( s.length() == 1) {
return s;
}
int strLen = s.length();
int left = 0;
int right = 0;
// 每次为中心的长度
int len = 1;
// 最大长度的开始位置
int maxStart = 0;
// 最大长度
int maxLen = 0;

for (int i = 1; i < strLen; i++) {
left = i - 1;
right = i + 1;
while (left >= 0 && s.charAt(left) == s.charAt(i)) {
len++;
left--;
}
while (right < strLen && s.charAt(right) == s.charAt(i)) {
len++;
right++;
}
while (left >= 0 && right < strLen && s.charAt(right) == s.charAt(left)) {
len = len + 2;
left--;
right++;
}
if (len > maxLen) {
maxLen = len;
maxStart = left;
}
len = 1;
}
return s.substring(maxStart + 1, maxStart + maxLen + 1);
}
}

但是以上的解法都有很多重复的计算,解决方法是使用动态规划,记录做过的重复计算。

对于一个子串而言,如果它是回文串,并且长度大于2,那么将它首尾的两个字母去除之后,它仍然是个回文串。

动态规划主要是找到初始状态和状态转移方程。初始状态 i=j ,此时dp[i][j]=true ,
状态转移方程dp[i][j]=true 并且(i-1)(j+1)两个位置为相同的字符,此时 dp[i-1][j+1]=true

注意填充顺序。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Solution {

public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}

int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
boolean[][] dp = new boolean[len][len];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}

char[] charArray = s.toCharArray();
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= len; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= len) {
break;
}

if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}

// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}

官方讲解
https://leetcode.cn/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/

链接:https://leetcode.cn/problems/longest-palindromic-substring