leetcode 5. 最长回文子串
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
提示:
-
1 <= s.length <= 1000
-
s
仅由数字和英文字母组成
Related Topics
字符串
动态规划
思路1: 动态规划
参考leetcode:
分析:
对于一个长度大于2的字符串而言,如果去掉首尾的字符,它仍然是回文串,那么只要首尾字符相等,这个字符就是回文串。
我们可以使用二维数组来表示字符串i到j([i,j])是否为回文串。
-
P(i,j) 表示字符串i到j的位置是否为回文串。那么 P(i,j) = p(i+1,j-1) + (字符i等于字符j)
-
想要求出i到j是否为回文串, 那么必须先求出i+1到j-1,所以我们必须求出长度从1到length的字串是否为回文串。
-
-
特殊情况:
-
当i=j,也就是一个字符的子串时,那么它就是回文串。
-
当j=i+1时,此时P(i,j) = 字符i等于字符j,无需判断P(i-1,j-1)
-
-
只需要保存最长回文串的起始位置和最大长度即可。
public String longestPalindrome(String s) { int length = s.length(); boolean[][] dp = new boolean[length][length]; int begin = 0; int maxLen = 1; char[] arrays = s.toCharArray(); //子串的长度为size 需要判断每一个子串是否是回文串 for(int size = 1; size <= length;size++){ //判断以i为起点 长度为size的子串 终点为 i+size - 1 for(int i = 0; i + size -1 < length;i++ ){ //计算子串的终点位置 int j = i + size -1; if(arrays[i] == arrays[j]){ //特殊情况 if(size <= 2){ dp[i][j] = true; }else{ //正常情况 Si == Sj && dp[i+1,j-1] dp[i][j] = dp[i+1][j-1]; } }else{ dp[i][j] = false; } //如果是回文串 并且大于最大长度 更新begin和maxLen if(dp[i][j] && size > maxLen){ begin = i; maxLen = size; } } } return s.substring(begin,begin+maxLen); } 解答成功: 执行耗时:167 ms,击败了40.87% 的Java用户 内存消耗:44.7 MB,击败了14.36% 的Java用户
思路2:中心扩展算法
在思路1:边界是子串长度为1或者为2的情况。我们可以从这2种情况开始向外扩展,因为P(i,j) = P(i+1,,j-1),当P(i,j)不成立时,那么所有以P(i,j)为中心的子串都不会是回文串,就不需要判断了。
class Solution { public String longestPalindrome(String s) { int begin = 0; int maxLen = 1; int length = s.length(); int len1,len2,len; for(int i = 0 ; i < length;i++){ len1 = expandCenter(s,i,i);//长度为1的边界情况 len2 = expandCenter(s,i,i+1);//长度为2的边界情况 len = Math.max(len1,len2); //计算起始位置 //举例:长度为1的边界情况向外扩展的长度为奇数 abcba 那么a的起始位置 2- 5/2( 5/2和4/2结果相等) //长度为2的边界情况向外扩展为偶数 bccb 那么b的起始位置 1-(4-1)/2 //所以我们可以统计起始位置为 begin = i - (len-1)/2 if(len > maxLen){ begin = i - ((len-1)>>1); maxLen = len; } } return s.substring(begin,begin+maxLen); } /** * 以left到right为中心,向外扩展 * @param s * @param left * @param right * @return */ public int expandCenter(String s,int left,int right){ while(left>=0 && right < s.length() && s.charAt(left) == s.charAt(right)){ left--; right++; } //当退出循环后 是回文串的区间是[left+1,right-1] //[a,b]范围内的字符长度为b-a+1 ,所以上述为right-1-(left+1)+1 = right - left - 1 return right - left - 1; } }