给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
这个题的解题思路是动态规划。
状态方程: dp[ i ][ j ]表示字符串s[ i, j ]的子串是否是一个回文串,如果是,那么值为true,否则为false。
转移方程: 当子串的长度大于等于2的时候,dp[ i ][ j ] = dp[ i + 1][ j - 1] && (chars[ i ] == chars[ j ])。即 [ i , j ]这个子串是否是一个回文串,主要是取决于首尾两个字符是否相等以及中间的子串是否是一个回文串。当子串的长度小于2的时候,那么必然dp[ i ][ j ]是true.
返回值: 返回长度最长的回文子串,所以只要我们知道了长度最长的回文子串的长度maxLen 以及这个回文子串的左边界begin,那么我们就可以知道这个回文子串的右边界是maxLen + i - 1,进而我们就可以利用substring这个方法,得到长度最长的回文子串了。
对应的代码:
class Solution {
public String longestPalindrome(String s) {
if(s == null || s.length() < 2)
return s;//如果字符串s为空,或者长度小于2,即只有一个字符,或者空字符的时候,必然是一个回文串,直接返回s
char[] chars = s.toCharArray();
/*
max_len的初始值是1,是因为进行这一步的时候,表明了字符串s的长度大于等于2,这时候
回文子串的长度的最小值就是1
*/
int i,j,begin = 0,len,subLen,max_len = 1;
len = chars.length;
boolean[][] dp = new boolean[len][len];//表示[i,j]的子串是否是一个回文串
/*
这一步并没有必要进行,之所以有这一步,只是说明长度为1的子串也是一个回文串,避免再下面
的for循环中dp[i][j] = dp[i + 1][j - 1]中发生错误。但是并没有必要,因为if(subLen <= 3)
就已经避免了这种情况了,从而使得即便是长度为1的子串,也变成了true。
for(i = 0; i < len; i++){
for(j = 0; j < len; j++)
dp[i][j] = true;//因为dp[i][i]就是一个回文串(即自身这个字符)
}
*/
for(subLen = 2; subLen <= len; subLen++){
//子串的长度从2开始,获取所有长度为subLen的子串,判断是否是一个回文子串
for(i = 0; i < len; i++){
j = subLen + i - 1;//获取i作为起点的子串的终点(subLen = j - i + 1得出)
if(j >= len)//如果发生了越界,那么说明i为起点,并没有办法构成长度为subLen的子串
break;
if(chars[i] != chars[j]){
//如果i、j对应的字符不相等等,那么说明不是回文子串,直接进入下一次循环
dp[i][j] = false;
continue;
}else{
if(subLen <= 3)
dp[i][j] = true;//如果当前的子串长度小于等于3,那么并且首尾两个字符相等,那么说明是一个回文子串
else
dp[i][j] = dp[i + 1][j - 1];
}
if(dp[i][j]){
/*
这个if判断可以没有subLen > max_len,因为最外层的subLen是逐渐递增的,那么退出第二层
循环的时候必然存在subLen大于max_len。如果if判断是if(dp[i][j] && subLen >
max_len),是为了获得第一个长度为subLen的回文子串.
*/
max_len = subLen;
begin = i;
}
}
}
return s.substring(begin,begin + max_len);
}
}
运行结果:
LeetCode 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:
输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
提示:
输入的字符串长度不会超过 1000 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
基于上面的思路,这道题对应的代码为:
class Solution {
public int countSubstrings(String s) {
if(s == null || s.length() == 0)
return 0;
else if(s.length() == 1)
return 1;
char[] chars = s.toCharArray();
int i,j,len,subLen,count;
len = chars.length;
/*
count一开始之所以初始化时len,是因为下面for循环subLen是从2开始循
环的,即子串的长度从2开始,因为子串长度为1的时候必然是回文串
*/
count = len;
boolean[][] dp = new boolean[len][len];//标记[i,j]这个子串是否是一个回文串
for(subLen = 2; subLen <= len; subLen++){
for(i = 0; i < len; i++){
j = subLen + i - 1;//获取长度为subLen的右边界
if(j >= len)
break;//如果发生了越界,那么直接退出循环
if(chars[i] != chars[j]){
//如果前后两个字符不相同,那么说明不是回文串,直接进入下一步循环
dp[i][j] = false;
continue;
}else{
if(subLen <= 3)//如果首尾两个字符相等,并且回文串的长度小于等3,那么说明是一个回文串
dp[i][j] = true;
else//如果回文串长度大于3,并且首尾两个字符相等,那么这个范围是否是一个回文串,取决于[i + 1,j - 1]是否为回文串
dp[i][j] = dp[i + 1][j - 1];
}
if(dp[i][j])//如果长度为subLen的[i,j]的子串是一个回文串,那么count++
count++;
}
}
return count;
}
}
运行结果: