LeetCode 最长回文子串
@author:Jingdai
@date:2020.11.13
题目描述(5题)
给定一个字符串
s
,找到s
中最长的回文子串。你可以假设s
的最大长度为 1000。
示例输入
"babad"
示例输出
"bab"
注意:“aba” 也是一个有效答案。
思路及代码
动态规划
首先看这个题用动态规划怎么做。动态规划最重要的就是要找到状态转移方程。对于一个串,如果它是一个回文串,则从它前后各去掉一个字符(不考虑边界问题),它仍然是一个回文串。即若
s[i]-s[j]
是回文串,则s[i+1]-s[j-1]
也是回文串;反过来,如果要使s[i]-s[j]
是回文串,需要s[i]
和s[j]
相等并且s[i+1]-s[j-1]
是回文串。这里用一个二维数组
isPalindrome
表示子串是否是一个回文串,isPalindrome[i][j]
为true
代表s[i]-s[j]
是一个回文串。则状态转移方程为:初始条件也很容易得到,就是当
i
和j
相等时,isPalindrome[i][j]
一定是true
。这里需要注意一点,就是遍历顺序的问题,
isPalindrome[i][j]
计算时需要用到isPalindrome[i+1][j-1]
的结果,所以isPalindrome[i+1][j-1]
必须要先遍历到,而isPalindrome[i+1][j-1]
在isPalindrome[i][j]
的左下位置,所以不能从上往下一行一行遍历,这里使用从左往右一列一列遍历。下面是具体的代码。
public String longestPalindrome(String s) { if (s == null || s.length() == 0) return null; if (s.length() == 1) return s; char[] chars = s.toCharArray(); int len = s.length(); boolean[][] isPalindrome = new boolean[len][len]; for (int i = 0; i < len; i++) { isPalindrome[i][i] = true; } int longestLen = 1; int start = 0; int end = 0; for (int j = 1; j < len; j++) { for (int i = 0; i < j; i++) { if (j == i+1) { isPalindrome[i][j] = chars[i] == chars[j]; } else { isPalindrome[i][j] = isPalindrome[i+1][j-1] && chars[i] == chars[j]; } if (isPalindrome[i][j] && j-i+1 > longestLen) { longestLen = j-i+1; start = i; end = j; } } } return s.substring(start, end+1); }
中心扩散方法
中心扩散方法正如它的名字一样,需要找到每个中心的回文串的长度,然后从这些长度中找到最长的返回。
如图,中心的选择有两种,一种是就是元素本身,而另一种是两个元素中间的间隙。这里我们用
left
和right
两个变量表示,当left
和right
相等时,代表是元素本身;当right=left+1
时,表示中心是两个元素的间隙。选择完中心后,对于每个中心,如果两边的字母相等,使该中心回文串的长度加2,否则直接返回。遍历整个字符串的所有中心找到最大的长度返回即可。
这里需要注意一点就是两种中心选择的回文串长度初始值不一样,写代码时需要判断一下。
下面是具体的代码。
public String longestPalindrome(String s) { if (s == null || s.length() == 0) return null; if (s.length() == 1) return s; char[] chars = s.toCharArray(); int start = 0; int end = 0; int longestLen = 1; int tempOddLen = 0; int tempEvenLen = 0; int tempMaxLen = 0; for (int i = 0; i < chars.length - 1; i++) { tempOddLen = palindromeLength(chars, i, i); tempEvenLen = palindromeLength(chars, i, i+1); tempMaxLen = tempOddLen > tempEvenLen ? tempOddLen : tempEvenLen; if (tempMaxLen > longestLen) { longestLen = tempMaxLen; start = i - (longestLen - 1) / 2; end = start + longestLen - 1; } } return s.substring(start, end+1); } public int palindromeLength(char[] s, int left, int right) { int len = 0; if (left == right) len = -1; while (left >=0 && right <= s.length - 1 && s[left] == s[right]) { left--; right++; len += 2; } return len; }