题目
给你一个字符串 s,找到 s 中最长的回文子串。
题解
动态规划
使用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示字符串s[i,j]是否是回文串
写出状态转移方程
d
p
[
i
]
[
j
]
=
d
p
[
i
+
1
]
[
j
−
1
]
&
s
[
i
]
=
=
s
[
j
]
dp[i][j]=dp[i+1][j-1]\&s[i]==s[j]
dp[i][j]=dp[i+1][j−1]&s[i]==s[j]
但子串
s
[
i
,
j
]
s[i,j]
s[i,j]是回文串的时候,其
s
[
i
+
1
,
j
−
1
]
s[i+1,j-1]
s[i+1,j−1]也是回文串,且s[i]和s[j]是相同字符。
上述转移方程针对子串长度大于2,即j-i+1>2.对于边界情况,定义:
d
p
[
i
]
[
i
]
=
t
r
u
e
d
p
[
i
]
[
i
+
1
]
=
(
s
[
i
]
=
=
s
[
i
+
1
]
)
dp[i][i]=true\\ dp[i][i+1]=(s[i]==s[i+1])
dp[i][i]=truedp[i][i+1]=(s[i]==s[i+1])
这个动态规划不是从字符串左到右,是从字符串长度短的开始向长的转移,因此最外层的遍历表示字符串长度
class Solution {
public String longestPalindrome(String s) {
int size = s.length();
boolean dp[][] = new boolean[size][size];
char charString[] = s.toCharArray();
int maxLen = 0;
int begin = 0;
// 边界 长度为1的是回文串
for(int i = 0; i < size; i++){
dp[i][i] = true;
}
// v 代表 i j 之间的间隔
for(int v = 1; v < size; v++){
for(int i = 0; i < size - v; i++){
if(charString[i] == charString[i+v]){
if(v == 1){
dp[i][i+v] = true;
}else{
dp[i][i+v] = dp[i+1][i+v-1];
}
}else{
dp[i][i+v] = false;
}
if(dp[i][i+v] && maxLen < v){
maxLen = v;
begin = i;
}
}
}
return s.substring(begin, begin+maxLen+1);
}
}
中心扩散法
只有一个元素的边界条件下满足是回文串,从每一个元素(边界条件)出发,逐步每次向左右两侧扩散,直到左右两侧的新增元素不相等为止。
注意两个两个元素也是边界条件,子串长度为2n
class Solution {
public String longestPalindrome(String s) {
int size = s.length();
char charArray[] = s.toCharArray();
int maxlen = 0;
int begin = 0;
for(int i = 0; i < size; i++){
int len1 = expandSubString(s, i, i);
int len2 = expandSubString(s, i, i+1);
if (maxlen < Math.max(len1, len2)){
maxlen = Math.max(len1, len2);
begin = i - (maxlen-1) / 2;
}
}
return s.substring(begin, begin+maxlen);
}
public int expandSubString(String s, int left, int right){
while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
left--;
right++;
}
return right - left + 1 -2;
}
}