最长回文子串(Longest Palindromic Substring)
轉載自??最長回文子串(Longest Palindromic Substring)——三種時間復雜度的解法
????子串:小于等于原字符串長度由原字符串中任意個連續字符組成的子序列
????回文:關于中間字符對稱的文法,即“aba”(單核)、“cabbac”(雙核)等
????最長回文子串:1.尋找回文子串;2.該子串是回文子串中長度最長的。
一、O(n^3)時間復雜度方法——暴力求解
1.思想:
????1)從最長的子串開始,遍歷所有該原字符串的子串;
????2)每找出一個字符串,就判斷該字符串是否為回文;
????3)子串為回文時,則找到了最長的回文子串,因此結束;反之,則繼續遍歷。
2.時間復雜度解釋:
????遍歷字符串子串:嵌套一個循環、O(n^2);???
????判斷是否為回文:再次嵌套一個循環、O(n^3)。
3.java代碼詳解:?
???public static String longestPalindrome(String s) {if(s.length() <= 1)return s;for(int i = s.length();i > 0; i--) {//子串長度for (int j = 0; j <= s.length() - i; j++) {String sub = s.substring(j , i + j);//子串位置int count = 0;//計數,用來判斷是否對稱for (int k = 0; k < sub.length() / 2; k++) {//左右對稱判斷if (sub.charAt(k) == sub.charAt(sub.length() - k - 1))count++;}if (count == sub.length() / 2)return sub;}}return "";//表示字符串中無回文子串}?
二、O(n^2)時間復雜度方法——從中心向外擴散
1.思想:
?????1)將子串分為單核和雙核的情況,單核即指子串長度為奇數,雙核則為偶數;
????2)遍歷每個除最后一個位置的字符index(字符位置),單核:初始low = 初始high = index,low和high均不超過原字符串的下限和上限;判斷low和high處的字符是否相等,相等則low++、high++(雙核:初始high = 初始low+1 = index + 1);
????3)每次low與high處的字符相等時,都將當前最長的回文子串長度與high-low+1比較。后者大時,將最長的回文子串改為low與high之間的;
????4)重復執行2)、3),直至high-low+1 等于原字符串長度或者遍歷到最后一個字符,取當前截取到的回文子串,該子串即為最長的回文子串。
2.時間復雜度解釋:
???遍歷字符:一層循環、O(n-1);
???找以當前字符為中心的最長回文子串:嵌套兩個獨立循環、O(2n*(n-1)) = O(n^2)。
3.java代碼詳解:
private static int maxLen = 0;private static String sub = "";public static String longestPalindrome(String s) {if(s.length() <= 1)return s;for(int i = 0;i < s.length()-1;i++){findLongestPalindrome(s,i,i);//單核回文findLongestPalindrome(s,i,i+1);//雙核回文}return sub;}public static??void findLongestPalindrome(String s,int low,int high){while (low >= 0 && high <= s.length()-1){if(s.charAt(low) == s.charAt(high)){if(high - low + 1 > maxLen){maxLen = high - low + 1;sub = s.substring(low , high+1);}low --;//向兩邊擴散找當前字符為中心的最大回文子串high ++;}elsebreak;}}?
三、O(n)時間復雜度方法——Manacher算法
1.思想:
????1)將原字符串S的每個字符間都插入一個永遠不會在S中出現的字符(本例中用“#”表示),在S的首尾也插入該字符,使得到的新字符串S_new長度為2*S.length()+1,保證Len的長度為奇數(下例中空格不表示字符,僅美觀作用);
????????例:S:???????a??a??b??a??b??b??a
????????S_new:????????#??a??#??a??#??b??#??a??#??b??#??b??#??a??#
????2)根據S_new求出以每個字符為中心的最長回文子串的最右端字符距離該字符的距離,存入Len數組中,(i為回文中間位置,r為回文右端位置)即S_new[i]—S_new[r]為S_new[i]的最長回文子串的右段(S_new[2i-r]—S_new[r]為以S_new[i]為中心的最長回文子串),Len[i] = r - i + 1;
????????S_new:????????#??a??#??a??#??b??#??a??#??b??#??b??#??a??#
????????Len:????????????1??2??3??2??1???4??1??4??1??2??5???2??1??2??1
????????Len數組性質:Len[i] - 1即為以Len[i]為中心的最長回文子串在S中的長度。在S_new中,以S_new[i]為中心的最長回文子串長度為2Len[i] - 1,由于在S_new中是在每個字符兩側都有新字符“#”,觀察可知“#”的數量一定是比原字符多1的,即有Len[i]個,因此真實的回文子串長度為Len[i] - 1,最長回文子串長度為Math.max(Len) - 1。
????3)Len數組求解(線性復雜度(O(n))):
???????a.遍歷S_new數組,i為當前遍歷到的位置,即求解以S_new[i]為中心的最長回文子串的Len[i];
???????b.設置兩個參數:sub_midd = Len.indexOf(Math.max(Len)表示在i之前所得到的Len數組中的最大值所在位置、sub_side = sub_midd + Len[sub_midd] - 1表示以sub_midd為中心的最長回文子串的最右端在S_new中的位置(原文作者算法思想錯誤,sub_midd是最右回文的右邊界)。起始sub_midd和sub_side設為0,從S_new中的第一個字母開始計算,每次計算后都需要更新sub_midd和sub_side;
??????c.當i < sub_side時,取i關于sub_midd的對稱點j(j = 2sub_midd - i,由于i <= sub_side,因此2sub_midd - sub_side <= j <= sub_midd);當Len[j] < sub_side - i時,即以S_new[j]為中心的最長回文子串是在以S_new[sub_midd]為中心的最長回文子串的內部,再由于i、j關于sub_midd對稱,可知Len[i] = Len[j];
????當Len[j] >= sub.side - i時說明以S_new[i]為中心的回文串可能延伸到sub_side之外,而大于sub_side的部分還沒有進行匹配,所以要從sub_side+1位置開始進行匹配,直到匹配失敗以后,從而更新sub_side和對應的sub_midd以及Len[i];
????????d.當i > sub_side時,則說明以S_new[i]為中心的最長回文子串還沒開始匹配尋找,因此需要一個一個進行匹配尋找,結束后更新sub_side和對應的sub_midd以及Len[i]。
2.時間復雜度解釋:
????????算法只有遇到還沒匹配的位置時才進行匹配,已經匹配過的位置不再進行匹配,因此大大的減少了重復匹配的步驟,對于S_new中的每個字符只進行一次匹配。所以該算法的時間復雜度為O(2n+1)—>O(n)(n為原字符串的長度),所以其時間復雜度依舊是線性的。
3.java代碼詳解:
public String longestPalindrome(String s) {List<Character> s_new = new ArrayList<>();for(int i = 0;i < s.length();i++){s_new.add('#');s_new.add(s.charAt(i));}s_new.add('#');List<Integer> Len = new ArrayList<>();String sub = "";//最長回文子串int sub_midd = 0;//表示在i之前所得到的Len數組中的最大值所在位置int sub_side = 0;//表示以sub_midd為中心的最長回文子串的最右端在S_new中的位置Len.add(1);for(int i = 1;i < s_new.size();i++){if(i < sub_side) {//i < sub_side時,在Len[j]和sub_side - i中取最小值,省去了j的判斷int j = 2 * sub_midd - i;if(j >= 2 * sub_midd - sub_side &&??Len.get(j) <= sub_side - i){Len.add(Len.get(j));}elseLen.add(sub_side - i + 1);}else//i >= sub_side時,從頭開始匹配Len.add(1);while( (i - Len.get(i) >= 0 && i + Len.get(i) < s_new.size()) && (s_new.get(i - Len.get(i)) == s_new.get(i + Len.get(i))))Len.set(i,Len.get(i) + 1);//s_new[i]兩端開始擴展匹配,直到匹配失敗時停止//原文作者算法思想錯誤,這里應該是Len.get(i) + i - 1 >= Len.get(sub_midd) + sub_midd - 1,求最右端回文邊界if(Len.get(i) >= Len.get(sub_midd) ){//匹配的新回文子串長度大于原有的長度sub_side = Len.get(i) + i - 1;sub_midd = i;}}sub = s.substring((2*sub_midd - sub_side)/2,sub_side /2);//在s中找到最長回文子串的位置return sub;}
?
總結
以上是生活随笔為你收集整理的最长回文子串(Longest Palindromic Substring)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 萨摩耶怎么训练 萨摩耶可以这样训练
- 下一篇: How to Delete a Wix