题目来源:Leetcode 5.最长回文子串
DP定义:
容易想到,用一个二维数字dp[i][j]来表示s[i:j]是否是回文串,如s=“daba”。dp[1][3]=1表示"aba"为回文串;
递归条件
想要判断字符串"aba"是否为回文串,只要判断字符串首和尾字符串相同"a"==“a”,并且中间的字符串"b"为回文串即可。
//如果坐标i和j的字符相同,并且夹在中间的字符串s[i:j]为回文串
if(s.charAt(i)==s.charAt(j) && dp[i-1][j-1]=1){
dp[i][j]=1;
}
代码
这里有一点需要注意,平常的双层递归都是用i,j作为左右边界遍历即可,如:
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
// i是字符串左边界,j是右边界
}
}
但是这里不能这样遍历。考虑字符串"aaa",依次遍历过程:dp[0][1]=1…dp[0][2]=1 dp[0][3]=0 不正确…
因为dp[0][3]是需要依据dp[1][2]的值的,然而dp[1][2]还没有比较。可以发现,较长的字符串会依赖较短的字符串,所以这里应该去枚举子串的长度,再去遍历左边界。
public String longestPalindrome(String s) {
int resLen = -1;
String res=s.substring(0,1);//默认答案为串的第一个字符,因为一个字符一定是回文串
//判断串的长度为1/2的特殊情况
if(s.length()<=1) return s;
if(s.length()==2){
return s.charAt(0)==s.charAt(1)?s:s.substring(0,1);
}
int[][] dp =new int[s.length()+1][s.length()+1];
for(int i=1;i<=s.length();i++) dp[i][i]=1; //dp[i:i]都是回文串
//按照子串长度来遍历,如果不按照长度遍历会出现 "aaaa"判断dp[0:4]时dp[1:3]还没有初始化
for(int len=2;len<=s.length();len++){
//枚举左节点
for(int i=0;i<s.length()-1;i++){
if(i+len-1 >=s.length()){
//越界
break;
}
//判断s[i: i+len-1]是否是回文串
int j = i+len-1;//右端点
//两端相同,则去判断中间的串是否是回文串。
if(s.charAt(i)==s.charAt(j)){
//串长度为2 或 3(一定是回文串) ,或者 dp[i+1][j-1]==1
if(j==i+1||j==i+2||dp[i+1][j-1]==1){
dp[i][j]=1;
//如果长度比之前的最大长度更长,则更新
if(j-i+1 >resLen){
resLen = j-i+1;
res=s.substring(i,j+1);
}
}
}
}
}
return res;
}