题目:
给定一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba"也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
方法1:暴力求解
思路:可以通从两端到中间遍历字符串,如果碰到字符串是回文串,则该回文串一定是是最长回文串。
效果:判断的整个过程其实有三个内部循环,时间复杂度接近 O(n^3) ,空间复杂度O(n)
public class LongestPalindrome1 { //利用两个for循环来遍历所有的子串,并且在里面再加一层while循环用于判断字符串是否是回文串 public String longestPalindrome(String s) { for (int size = s.length(); size > 0; size--) { for (int low = 0, hight = low+size-1; hight < s.length(); low++,hight++) { if (isPalindrome(s,low,hight)) { return s.substring(low, hight+1); } } } return s.substring(0, 1); } //判断一个字符串是否是回文字符 private boolean isPalindrome(String s, int low, int hight) { while (low < hight) { if(s.charAt(low) == s.charAt(hight)){ low++; hight--; }else { return false; } } return true; } }
方法2:动态规划思想
思路:如果s(i,j)是回文串,则table[i][j] = true;否则定义为false。如何计算table[i][j]呢,我们可以首先检查table[i+1][j-1]是否为true,及s[i]是否等于s[j]。
--------------------------------------------------这个思想其实和前面的求解最长公共子序列很像。
效果:时间复杂度O(n^2),空间O(n^2)
public class LongestPalindrome { public static String longestPalSubstr(String s){ int n = s.length(); boolean[][] table = new boolean[n][n]; //所有的一个字符肯定是回文串 int maxLength = 1; //组成的boolean矩阵的对角都肯定为true for (int i = 0; i < n; i++) { table[i][i] = true; } int start = 0; //如果字符串长度小于2,则直接处理 for (int i = 0; i < n - 1; ++i) { if (s.charAt(i) == s.charAt(i +1)) { table[i][i+1] = true; start = i; maxLength = 2; } } //如果字符串长度大于2,则开始遍历处理,主要是通过两层for循环 //检查字符串长度大于2的字符串,其中k表示子字符串的长度 for (int k = 3; k <= n; ++k) { //i表示子字符串中起始的位置 for (int i = 0; i < n - k + 1; i++) { //获取子字符串结束的位置, int j = i + k - 1; if (s.charAt(i) == s.charAt(j) && table[i+1][j-1]) { table[i][j] = true; if (k > maxLength) { maxLength = k; start = i; } } } } return s.substring(start, start+maxLength); } }
方法3:分奇偶中心讨论
思路:以某个元素为中心,分别计算偶数长度的回文最大长度和奇数长度的回文最大长度。
奇数有一个中心,偶数有两个中心点,从中心点向两端判断,找出最长的回文子串。
效果:时间复杂度O(n^2),空间O(1)
public class LongestPalindrome { public static String longestPalSubstr(String s){ //最后字符串的长度 int maxLength = 1; int start = 0; int len = s.length(); int low ,hight; //这里面用i表示中心点的位置 for (int i = 0; i < len; i++) { //首先查找最长的偶数回文字符串,有两个中心点,分别为 i -1、 i low = i -1; hight = i; while (low >= 0 && hight < len && s.charAt(low) == s.charAt(hight)) { if (hight - low + 1 > maxLength) { maxLength = hight - len + 1; start = low; } --low; ++hight; } //其次寻找最长的奇数的最长回文字符串,中心点以i为主 low = i - 1; hight = i + 1; while (low >= 0 && hight < len && s.charAt(low) == s.charAt(hight)) { if (hight - low + 1 > maxLength) { maxLength = hight - low + 1; start = low; } --low; ++hight; } } //起始和结束的位置 return s.substring(start, start + maxLength); } }