5. 最长回文子串
方法一、DP
假设dp[i][j]表示从i到j下标是否是回文串
于是有dp[i][j]=dp[i+1][j-1]^(s[i]==s[j])
时间O(n^2),空间O(n^2)
1 public String longestPalindrome(String s) { 2 int len = s.length(); 3 if(len<2){ 4 return s; 5 } 6 7 boolean dp[][] = new boolean[len][len]; 8 // 每个字符自身都是回文的 9 for(int i=0;i<len;i++){ 10 dp[i][i]=true; 11 } 12 // 由于每个字符自身回文,因此最短也是1 13 int maxLen = 1,begin=0; 14 for(int j=1;j<len;j++){ 15 for(int i=0;i<j;i++){ 16 // 首尾不相等,则必然不回文 17 if(s.charAt(i)!=s.charAt(j)){ 18 dp[i][j]=false; 19 }else{ 20 // 首尾相同的情况下,前后相差位数不超过2的时候,必然是回文的 21 if(j-i<3){ 22 dp[i][j]=true; 23 }else{ 24 // 这里可以由之前的状态获取到当前的状态 25 dp[i][j]=dp[i+1][j-1]; 26 } 27 } 28 // 每次循环后更新最长回文子串的长度与起始位置 29 if(dp[i][j]==true && (j-i+1)>maxLen){ 30 maxLen = j-i+1; 31 begin = i; 32 } 33 } 34 } 35 return s.substring(begin,begin+maxLen); 36 }
方法二、中心拓展
针对每个元素,我们以此为中心,使用双指针法向两边拓展。
直到不回文为止,每次结束更新最长回文子串的长度与起始位置
时间O(n^2),空间O(1)
1 class Solution { 2 public String longestPalindrome(String s) { 3 int len=s.length(); 4 if(len<2){ 5 return s; 6 } 7 8 int start=0,end=0; 9 for(int i=0;i<len;i++){ 10 // 需要考虑奇数和偶数2种情况 11 int len_odd = expandCenter(s,i,i); 12 int len_even = expandCenter(s,i,i+1); 13 int maxLen = Math.max(len_odd,len_even); 14 if(maxLen>end-start){ 15 start = i-(maxLen-1)/2; 16 end = i+maxLen/2; 17 } 18 } 19 return s.substring(start,end+1); 20 } 21 22 public int expandCenter(String s,int left,int right){ 23 while(left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){ 24 left--; 25 right++; 26 } 27 // 这里由于最后一次循环时start和end分别做了加减,所以是end-start-1+2=start-left-1 28 return right-left-1; 29 } 30 }
方法三、马拉车
争取早日不再是一只菜鸡