LeetCode 5 (Longest Palindromic Substring)
Longest Palindromic Substring(最大回文字串)
1、題目描述:
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
給出一個字串s,找出長度最大的迴文子串,s的最大長度小於1000。
2、摘要:
下面介紹了幾種方法實現:迴文,動態規劃和字串操作。迴文的定義:一個字串從兩個方向讀,它的內容是相同的。例如:S = "aba"是迴文字串,而S = "abc"不是迴文字串。
3、解決方法:
方法1:Brute Force(暴力破解)
很明顯,暴力破解就是找到所有子串驗證它是否是迴文字串。
Java實現:
public boolean isPalindrome(String s) {
String ss = new StringBuilder(s).reverse().toString();
if (ss.equals(s)) {
return true;
}
return false;
}
public String longestPalindrome(String s) {
int longestLength = 0;
String longestSubString = "";
for (int i = 0; i < s.length(); i++) {
for (int j = i+1; j <= s.length(); j++) {
String subString = s.substring(i,j);
if (isPalindrome(subString) && subString.length()>longestLength){
longestLength = subString.length();
longestSubString = subString;
}
}
}
return longestSubString;
}
時間複雜度:
兩個for迴圈中巢狀了一個判斷迴文的過程,迴文判斷我使用的是StringBuilder的reverse()方法,時間複雜度一共是O(n^3)。比較不理想,提交上去會出現時間超時。
空間複雜度:兩個變數,複雜度為O(1)。
方法2:Longest Common Substring(最長的公共子串)
一些人可能會想出一個最快的方法,倒序字串s,然後與原字串對比,然後找出最長的公共子串,這個子串一定就是最長的迴文子串。
從表面上看這個方法是正確的,但是仔細想來並不是完全正確,例如S = "abacdfgdcaba",他和倒序的公共最長字元為 "abacd",然而這個並不是迴文字串。導致出現這個情況的原因是原字串中存在一個非迴文倒序副本。如果要排除這個影響,就要在候選字串中 檢查子串的索引是否與反向子串的原始索引相同,相同就保留,不同就捨棄。
首先實現尋找最長的公共子串,具體步驟參考:
https://blog.csdn.net/u010397369/article/details/38979077
具體實現思路就是把兩個字串組成一個二維陣列 ,如果兩個對應字元相等,就執行 temp[ i ][ j ] = temp[ i - 1 ][ j - 1] + 1。因為i-1或者j-1會越界,所以可以單獨處理。temp[ i ][ j ] 儲存的就是公共子串的長度。
Java實現
public String longestPalindrome_2(String s) {
if (s.equals("")) {
return "";
}
String ss = new StringBuilder(s).reverse().toString(); //倒序
int longestlength = 0;
int maxEnd = 0;
int[][] temp = new int[s.length()][ss.length()];
char[] s_char = s.toCharArray();
char[] ss_char = ss.toCharArray();
//原字串做列,倒序後的子串作為行
for (int i = 0; i < ss_char.length; i++) {
for (int j = 0; j < s_char.length; j++) {
if (s_char[i] == ss_char[j]) {
if (i == 0 || j == 0) {
temp[i][j] = 1;
} else {
temp[i][j] = temp[i - 1][j - 1] + 1;
}
}
if (temp[i][j] > longestlength) {
longestlength = temp[i][j];
maxEnd = i;
}
}
}
return s.substring(maxEnd - longestlength + 1, maxEnd + 1);
}
以上演算法只能實現尋找最長的公共子串,如果s="abc435cba",公共子串為"abc",但是這個不是迴文字串。為了解決這個問題,我們還要對比子串在倒序後的字串的位置和原字串的位置是否對應。
舉個例子,如果s="caba",s' = "abac",他們的最長迴文串為"aba","aba"在原字串中的位置為 1 2 3 ,在s'中的位置為 0 1 2,所以 aba 就是我們需要找的。當然我們不需要每個字元都判斷,我們只需要判斷末尾字元就可以。
如圖:
i
所指的字元a在原字串中的位置為beforeRev = length - i- 1 = 0
,beforeRev
就是在j
中為第一個字元位置,且 beforeRev + temp[i][j] - 1 =2
代表j
中最後一個字元的位置,如果位置與j
相等,aba就是要找的。我們可以寫出如下程式碼:
//動態規劃 (獲取最長迴文串) 需要和原字元對比位置
public String longestPalindrome_3(String s) {
if (s.length() <= 1) {
return s;
}
String ss = new StringBuilder(s).reverse().toString(); //倒序
int longestlength = 0;
int maxEnd = 0;
int[][] temp = new int[s.length()][ss.length()];
char[] s_char = s.toCharArray();
char[] ss_char = ss.toCharArray();
//原字串做列,倒序後的子串作為行
for (int i = 0; i < ss_char.length; i++) {
for (int j = 0; j < s_char.length; j++) {
if (s_char[i] == ss_char[j]) {
if (i == 0 || j == 0) {
temp[i][j] = 1;
} else {
temp[i][j] = temp[i - 1][j - 1] + 1;
}
}
if (temp[i][j] > longestlength) {
/*******************增加的部分***********************/
int beforeRev = s.length() - i - 1;
if (beforeRev + temp[i][j] - 1 == j) {
longestlength = temp[i][j];
maxEnd = i;
}
}
}
}
return s.substring(maxEnd - longestlength + 1, maxEnd + 1);
}
執行時間:
時間複雜度:兩個巢狀迴圈,O(n^2)
空間複雜度:一個二維陣列,O(n^2)
仔細觀察可以發現,我們判斷字元相等的只用到了temp[i][j],一行用過之後就棄置不用了。所以我們可以把空間複雜度優化到O(n),只需要把一個一維陣列重新賦值即可,因為正序賦值有可能覆蓋改後面需要使用的資料
比如a[3] = a[2]+1時,計算a[4]的時候a[3]的值就不是原來的了。所以我們需要從後往前計算,程式碼如下:
public String longestPalindrome_4(String s) {
if (s.equals("")) {
return "";
}
String ss = new StringBuilder(s).reverse().toString(); //倒序
int longestlength = 0;
int maxEnd = 0;
int[] temp = new int[s.length()];
char[] s_char = s.toCharArray();
char[] ss_char = ss.toCharArray();
for (int i = 0; i < s_char.length; i++) { //初始化第一行
temp[i] = (s_char[0] == ss_char[i]) ? 1 : 0;
}
for (int i = 0; i < s_char.length; i++) {
for (int j = ss.length() - 1; j >= 0; j--) {
if (s_char[i] == ss_char[j]) {
if (i == 0 || j == 0) {
temp[j] = 1;
} else {
temp[j] = temp[j - 1] + 1;
}
if (temp[j] > longestlength) {
/*******************增加的部分***********************/
int beforeRev = s.length() - j - 1;
if (beforeRev + temp[j] - 1 == i) {
longestlength = temp[j];
maxEnd = i;
}
}
} else {
temp[j] = 0;
}
}
}
return s.substring(maxEnd - longestlength + 1, maxEnd + 1);
}
執行時間:
時間複雜度:兩個巢狀迴圈,O(n^2)
空間複雜度:一個一維陣列,O(n)
方法3:擴充套件中心
我們觀察到一個迴文串是從一箇中心到兩邊的映象。所以,迴文串可以從一個字元(奇數)或兩個字元(偶數)的為中心擴充,它的中心總共有2n-1個。以i為中心左邊為left,右邊為right,先令left=right=i,滿足left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)時,left--;right++;向外擴充,直到結束。迴文的長度就是right - left - 1。實現如下:
Java實現:
//方法 擴充套件中心
public int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return right - left - 1;
}
public String longestPalindrome_6(String s) {
if (s == null || s.length() < 1)
return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i); //奇數
int len2 = expandAroundCenter(s, i, i + 1);//偶數
int len = Math.max(len1, len2);
if (len > end - start) {
//重新計算start 和end
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
執行時間如下:
因為只識別迴文序列,過濾掉了很大部分情況,雖然時間複雜度為o(n^2),但是執行效率更高。
時間複雜度:兩個巢狀迴圈,最壞的情況下,O(n^2)
空間複雜度:O(1)
參考:
https://leetcode.com/problems/longest-palindromic-substring/solution/
http://windliang.cc/2018/08/05/leetCode-5-Longest-Palindromic-Substring/
相關文章
- Leetcode5: Longest Palindromic Substring(最長迴文子串)LeetCode
- [LeetCode] 5. Longest Palindromic SLeetCode
- Leetcode 3 Longest Substring Without Repeating CharactersLeetCode
- Leetcode 3. Longest Substring Without Repeating CharactersLeetCode
- [LeetCode] 2414. Length of the Longest Alphabetical Continuous SubstringLeetCodeAlphabet
- Leetcode javascript 3 longest-substring-without-repeating-charactersLeetCodeJavaScript
- LeetCode Longest Substring Without Repeating Characters(003)解法總結LeetCode
- [LeetCode] 3. Longest Substring Without Repeating Characters 題解LeetCode
- D. Non-Palindromic Substring
- 【Leetcode】3. Longest Substring Without RepeatingCharacters無重最長子串LeetCodeGC
- #3 Longest Substring Without Repeating Characters[M]
- LeetCode3:Longest Substring Without Repeating Characters(無重複字元的最長子串)LeetCode字元
- SPOJ 1811 Longest Common Substring(字尾自動機)
- Leetcode 32 Longest Valid ParenthesesLeetCode
- Leetcode 14 Longest Common PrefixLeetCode
- [LeetCode] 32. Longest Valid ParenthesesLeetCode
- Leetcode 30 Substring with Concatenation of All WordsLeetCode
- [LeetCode] 674. Longest Continuous Increasing SubsequenceLeetCode
- Leetcode 298 Binary Tree Longest Consecutive SequenceLeetCode
- Leetcode 329. Longest Increasing Path in a MatrixLeetCode
- [LeetCode] 2831. Find the Longest Equal SubarrayLeetCode
- leetcode388. Longest Absolute File PathLeetCode
- [LeetCode] 2419. Longest Subarray With Maximum Bitwise ANDLeetCode
- [LeetCode] 3239. Minimum Number of Flips to Make Binary Grid Palindromic ILeetCode
- [LeetCode] 524. Longest Word in Dictionary through DeletingLeetCode
- LeetCode Longest Common Prefix(014)解法總結LeetCode
- [LeetCode] 3090. Maximum Length Substring With Two OccurrencesLeetCode
- C# 寫 LeetCode easy #14 Longest Common PrefixC#LeetCode
- leetcode學習筆記14 Longest Common PrefixLeetCode筆記
- [leetcode] 1624. Largest Substring Between Two Equal CharactersLeetCode
- LeetCode - 014 - 最長公共字首(longest-common-prefix)LeetCode
- Leetcode 329. Longest Increasing Path in a Matrix (python+cpp)LeetCodePython
- Leet Code 3. Longest Substring Without Repeating Characters (最長的沒有重複字元的子字串)字元字串
- LeetCode | 14. Longest Common Prefix的三種演算法LeetCode演算法
- Js的substring和C#的SubstringJSC#
- JavaScript substring()JavaScript
- [AtCoder Beginner Contest 363] D - Palindromic Number
- LeetCode(1297):子串的最大出現次數 Maximum Number of Occurrences of a Substring(Java)LeetCodeJava