問題の説明
文字列sを指定し、sで最長の回文部分文字列を見つけます。
例1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
例2:
输入:s = "cbbd"
输出:"bb"
例3:
输入:s = "a"
输出:"a"
例4:
输入:s = "ac"
输出:"a"
促す:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
問題解決のアイデア
部分文字列の場合、それが回文であり、長さが2より大きい場合、最初と最後の2文字を削除した後でも、それは回文のままです。たとえば、文字列{"ababa"}の場合、{"bab"}が回文文字列であることがすでにわかっている場合、{"ababa ''}は最初と最後の2文字が両方とも"であるため、回文文字列である必要があります。 a "。
この考えによれば、動的計画法を使用してこの問題を解決できます。
文字列の最初と最後の2文字が等しくない場合、その文字列は回文であってはなりません。文字列
の最初と最後の2文字が等しい場合は、判断を続ける必要があります。
- 内部の部分文字列が回文である場合、全体が回文です。
- 内部の部分文字列が回文でない場合、全体は回文ではありません。
つまり、頭と尾の文字が等しい場合、その中の部分文字列の回文プロパティは、状態遷移である部分文字列全体の回文プロパティによって決定されます。したがって、「状態」は、元の文字列の部分文字列が回文部分文字列であるかどうかとして定義できます。
ステップ1:状態
dp [i] [j]を定義して、部分文字列s [i…j]が回文部分文字列であるかどうかを示します。ここで、部分文字列s [i…j]は左閉区間と右閉区間として定義されます。 、これはs [i]およびs [j]と見なすことができます。
ステップ2:状態遷移方程式について考えます。
このステップでは、上記の分析に従って、(最初と最後の文字が等しいかどうかに応じて)分類して話し合います。
dp[i][i] = (s[i]==s[j]) abd dp[i][j] 当j-i<=1时
dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1] 当j-i>1时
実装コード
class Solution {
public String longestPalindrome(String s) {
int n=s.length();
char [] arrStr=s.toCharArray(); //将字符串转化为数组,便于运算
int maxi=0,maxj=0; //标记最长回文子串的下标
int maxlen=0;
//标记数组,标记从起点i到终点j之间的字符是否是回文子串,默认全为false
boolean [][] f=new boolean[n][n];
//这里i是终点,j是i之前的字符,即以i为终点,遍历i之前的字符是否为回文子串
//注意这里的顺序,一定要保证后面的回文子串利用到前面的回文子串时,前面的回文子串一定被计算出来过了。
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
//当i-j<=1时,判读那两个字符是否相等,相等即为回文子串,这里包含了i与j重合的情况
if(i-j<=1){
if(arrStr[i]!=arrStr[j]){
f[j][i]=false;
}else{
f[j][i]=true; //相等时如果大于当前最长回文子串的时候,就记录下来
if(maxlen<(i-j+1)){
maxlen=i-j+1;
maxi=i;
maxj=j;
}
}
}else{
//当i-j>1时,需要判断当前两个字符是否相等,并且还要判断子字符串是否为回文子串。
if(arrStr[i]==arrStr[j] && f[j+1][i-1]==true){
f[j][i]=true;
if(maxlen<(i-j+1)){
maxlen=i-j+1;
maxi=i;
maxj=j;
}
}else{
f[j][i]=false;
}
}
}
}
String res="";
for(int i=maxj;i<=maxi;i++){
res+=arrStr[i];
}
return res;
}
}