leetcode 5. 最长回文子串(Java版)

时间:2019-02-19
本文章向大家介绍leetcode 5. 最长回文子串(Java版),主要包括leetcode 5. 最长回文子串(Java版)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目描述(题目难度,中等)

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

示例 2:

输入: “cbbd”
输出: “bb”

示例代码

时间复杂度为O(n2)O(n^2),空间复杂度为O(1)O(1)

class Solution {
    	public boolean isPalindrome(String s, int b, int e){//判断s[b...e]是否为回文字符串
		int i = b, j = e;
		while(i <= j){
			if(s.charAt(i) != s.charAt(j)) return false;
			++i;
			--j;
		}
		return true;
	}
    public String longestPalindrome(String s) {
    	if(s.length() <=1){
    		return s;
    	}
    	int l = 1, j = 0, ll = 1;
    	for(int i = 1; i < s.length(); ++i){
             //下面这个if语句就是用来维持循环不变式,即ll恒表示:以第i个字符为尾的最长回文子串的长度
    		 if(i - 1 - ll >= 0 && s.charAt(i) == s.charAt(i-1-ll)) ll += 2;
    		 else{
    			 while(true){//重新确定以i为边界,最长的回文字串长度。确认范围为从ll+1到1
    				 if(ll == 0||isPalindrome(s,i-ll,i)){
    					 ++ll;
    					 break;
    				 }
    				 --ll;
    			 }
    		 }
    		 if(ll > l){//更新最长回文子串信息
     			l = ll;
     			j = i;
     		}
    	}
        return s.substring(j-l+1, j+1);//返回从j-l+1到j长度为l的子串
    }
}

思路解析

这是一道老题目了,本来想自己想出一个O(n)O(n)的解法出来。想了半天,很兴奋的觉得想到了一个O(n)O(n)的解法,结果拿去一运行,输出不正确,最后发现是因为自己的想法在细节上存在漏洞。其实整体的思路还是没问题的,上面的代码就是按原来的想法改的,修补了细节,不过时间复杂度变为了O(n2)O(n^2)
下面描述一下上面代码的思路:
假设输入的字符串为 S ,首先我借助两个变量来保存最长回文字串的信息,分别为 l 和 j。其中 l 表示最长回文子串的长度,j 表示最长回文子串的尾字符在 S 中的下标。这样最后算法结束后,便可通过 l 和 j 定位到最长回文子串,并返回算法结果。另外还使用变量 ll 表示以当前字符为边界的最长回文子串的长度。
算法的具体流程如下:
如果当前遍历到 S 的第 i(从0开始)个字符(此时的 l 和 j 保存的是 S[0…i-1]的最长回文子串的信息),通过第 i-1 个字符的 ll 来判断当前字符有没有增加前一个最长回文子串的长度,如果增加了,则 ll 自增 2,此时更新后的 ll 代表的就是以第 i 个字符为边界的最长回文子串的长度。如果没有增加,就需要重新确定以第 i 个字符为边界的最长回文子串的长度,确认范围为从 ll+1 到 1。
ll 更新后,比较 ll 与 l 的大小,如果 ll 变得比 l 要大,则将当前 ll 的值赋给 l,i 的值赋给 j。
上述流程的核心是,一定要在遍历的时候保证 ll 表示的一定是以当前字符为尾的最长回文子串的长度,这一点即本算法的循环不变式。遵循该循环不变式,遍历一遍 S 后即可得到结果。

最后提一下,最长回文子串问题是有 O(n) 时间复杂度的算法的,算法名字叫 Manacher 算法,又叫 “马拉车”算法,感兴趣的同学可以去搜索了解一下。