个人记录-LeetCode 5.Longest Palindromic Substring
2016-09-04 20:22
357 查看
问题
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
问题要求找到一个字符串中最长的回文。
常规解法:
假设字符串长度为N,
因为需要找出最长的回文字符串,因此可以先判断长度为N的字符串是否回文;
如果长度为N的字符串不是回文,就依次判断长度为N-1的字符串是否回文;
依次类推,知道找到第一个回文字符串。
由于长度是依次递减的,因此第一个找到的回文字符串,肯定是最长的。
对应代码如下:
这种做法是可行的,但显然算法复杂度达到了O(N3),不符合LeetCode的逼格。
进阶一:
前面方法存在的一个显著问题是,由于后一次比较无法利用前一次比较的信息,导致存在大量重复性的比较。
实际上,一个回文字符串以中心对称的子字符串也是回文的,因此我们应该先寻找短的回文串,并进行记录。下一次比较时,只需要比较两端增加的位置是否相等。
对应代码如下:
由于增加了历史信息的对比,整个算法的复杂度达到了O(N2)。
进阶二
上面两种方法,均是通过不断试探起点,试探长度来探索出最长回文串。第二种方法只是在判断回文串的方式上,优于第一种方法,带来的效率上的提升,但思路的原点是一致的。
现在我们换一个思路,将判断回文串和试探长度的动作结合起来。
整个算法的复杂度也是O(N2),但对内存的占用更小。
进阶四 Manacher算法
关于这个算法的原理,在CSDN上看到了一个解释的最好的博客,自己就不再赘述了:
http://blog.csdn.net/dyx404514/article/details/42061017
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
问题要求找到一个字符串中最长的回文。
常规解法:
假设字符串长度为N,
因为需要找出最长的回文字符串,因此可以先判断长度为N的字符串是否回文;
如果长度为N的字符串不是回文,就依次判断长度为N-1的字符串是否回文;
依次类推,知道找到第一个回文字符串。
由于长度是依次递减的,因此第一个找到的回文字符串,肯定是最长的。
对应代码如下:
public class Solution { public String longestPalindrome(String s) { String result = null; if (s != null) { int len = s.length(); //i表示每次的选择的字符串长度,依次递减 for (int i = len; i >= 1; --i) { //字符串首地址从0开始,依次增加 //取出长度为len的字符串 for (int j = 0; j <= len - i; ++j) { //判断字符串是否回文 if (isPalindrome(s.substring(j, j + i))) { return s.substring(j, j+i); } } } } return result; } //判断字符串是否回文,其实就是判断字符串是否对称相等 private boolean isPalindrome(String s) { boolean result = true; int len = s.length(); for (int i = 0; i < len/2; ++i) { if (s.charAt(i) != s.charAt(len - 1 - i)) { result = false; break; } } return result; } }
这种做法是可行的,但显然算法复杂度达到了O(N3),不符合LeetCode的逼格。
进阶一:
前面方法存在的一个显著问题是,由于后一次比较无法利用前一次比较的信息,导致存在大量重复性的比较。
实际上,一个回文字符串以中心对称的子字符串也是回文的,因此我们应该先寻找短的回文串,并进行记录。下一次比较时,只需要比较两端增加的位置是否相等。
对应代码如下:
public class Solution { public String longestPalindrome(String s) { String result = null; if (s != null) { int len = s.length(); if(len <= 1) { return s; } //compareCache[i][j]表示i~j位置的字符串是回文的,i为左下标,j为右下标 boolean[][] compareCache = new boolean[len][len]; compareCache[0][0] = true; for (int i = 1; i < len; ++i) { //一个长度初始化为true compareCache[i][i] = true; //右下标应该比左下标大,compareCache[i][i-1]无实际物理意义,仅为了便于处理长度为2的子串 compareCache[i][i-1] = true; } int leftIndex = 0; int rightIndex = 0; //subLen用于定义当前字符串长度 for (int subLen = 2; subLen <= len; ++subLen) { //i用于定义当前字符串起始位置 for (int i = 0; i <= len - subLen; ++i) { //当前字符串的子字符串为回文,并且两边新增字符相等时,该字符串也是回文的 //当长度为2时,不需要历史信息,因此上面对应compareCache初始化为true if (s.charAt(i) == s.charAt(i + subLen - 1) && compareCache[i + 1][i + subLen -2]) { compareCache[i][i + subLen - 1] = true; //历史长度小于当前长度,进行更新 if (rightIndex - leftIndex + 1 < subLen) { leftIndex = i; rightIndex = i + subLen -1; } } } } result = s.substring(leftIndex, rightIndex + 1); } return result; } }
由于增加了历史信息的对比,整个算法的复杂度达到了O(N2)。
进阶二
上面两种方法,均是通过不断试探起点,试探长度来探索出最长回文串。第二种方法只是在判断回文串的方式上,优于第一种方法,带来的效率上的提升,但思路的原点是一致的。
现在我们换一个思路,将判断回文串和试探长度的动作结合起来。
public class Solution { public String longestPalindrome(String s) { String result = null; if (s != null) { int len = s.length(); if(len <= 1) { return s; } //startWithMaxLen中分别存储回文字符串的起始地址和长度 int[] startWithMaxLen = new int[] {0, 1}; for (int i = 1; i <= len-1; ++i) { ////寻找以i为中心的奇数长度的回文 updateStartAndMaxLen(s, len, i - 1, i + 1, startWithMaxLen); //寻找以i-1,i为中点偶数长度的回文 updateStartAndMaxLen(s, len, i - 1, i, startWithMaxLen); } result = s.substring(startWithMaxLen[0], startWithMaxLen[0] + startWithMaxLen[1]); } return result; } private void updateStartAndMaxLen(String s, int len, int leftIndex,int rightIndex, int[] startWithMaxLen) { while (leftIndex >= 0 && rightIndex <= len - 1 && s.charAt(leftIndex) == s.charAt(rightIndex)) { leftIndex--; rightIndex++; } leftIndex++; rightIndex--; int curLen = rightIndex - leftIndex + 1; //更新数组 if (curLen > startWithMaxLen[1]) { startWithMaxLen[0] = leftIndex; startWithMaxLen[1] = curLen; } } }
整个算法的复杂度也是O(N2),但对内存的占用更小。
进阶四 Manacher算法
关于这个算法的原理,在CSDN上看到了一个解释的最好的博客,自己就不再赘述了:
http://blog.csdn.net/dyx404514/article/details/42061017
相关文章推荐
- 个人记录-LeetCode 3.Longest Substring Without Repeating Characters
- 个人记录-LeetCode 6.ZigZag Conversion
- 个人记录-LeetCode 22. Generate Parentheses
- 个人记录-LeetCode 27. Remove Element
- 个人记录-LeetCode 26. Remove Duplicates from Sorted Array
- 个人记录-LeetCode 1.Two Sum
- 个人记录-LeetCode 31. Next Permutation
- 个人记录-LeetCode 20. Valid Parentheses
- 个人记录-LeetCode 19. Remove Nth Node From End of List
- 个人记录-LeetCode 29. Divide Two Integers
- 个人记录-LeetCode 7.Reverse Integer
- 个人记录-LeetCode 12. Integer to Roman
- 个人记录-LeetCode 11. Container With Most Water
- 个人记录-LeetCode 21. Merge Two Sorted Lists
- 个人记录-LeetCode 16. 3Sum Closest
- 个人记录-LeetCode 4.Median of Two Sorted Arrays
- 个人记录-LeetCode 10.Regular Expression Matching
- 个人记录-LeetCode 14. Longest Common Prefix
- 个人记录-LeetCode 24. Swap Nodes in Pairs
- 个人记录-LeetCode 30. Substring with Concatenation of All Words