標籤:track sso targe 複雜 mod return name i+1 turn
背景
近期開始研究演算法,於是在leetcode上做演算法題,第五題Longest Palindromic Substring便是關於迴文子串的。
什麼是迴文字串
迴文字串是指將該字串前後顛倒之後和該字串一樣的字串。比如:a,aaaa,aba,abba…
最長迴文子串
要求最長迴文子串,就須要遍曆每個子串,時間複雜度是O(N2);推斷字串是不是迴文,時間複雜度是O(N),這種話演算法的時間複雜度就是O(N3).
我剛開始想到的就是中心擴充法,代碼例如以下:
public static String getLongestPalindrome(String str) { if(str.isEmpty() || str.length() == 1) { return str; } String longest = str.substring(0, 1); for (int i = 0; i < str.length(); i++) { // get longest palindrome with center of i String tmp = helper(str, i, i); if (tmp.length() > longest.length()) { longest = tmp; } // get longest palindrome with center of i, i+1 tmp = helper(str, i, i + 1); if (tmp.length() > longest.length()) { longest = tmp; } } return longest; } private static String helper(String str, int begin, int end) { while (begin >= 0 && end <= str.length() - 1 && str.charAt(begin) == str.charAt(end)) { begin--; end++; } String result = str.substring(begin + 1, end); return result; }
中心擴充法的時間複雜度為O(N2).
寫完之後一直在想,有沒有更厲害的辦法呢。能將時間複雜度殺到O(N)呢,一直想也想不到,後來上網搜到了傳說中的Manacher演算法。
我們先來看一下代碼:
public static int[] getPalindromeLength(String str) { StringBuilder newStr = new StringBuilder(); newStr.append("#"); for(int i = 0; i < str.length(); i++) { newStr.append(str.charAt(i)); newStr.append("#"); } int[] rad = new int[newStr.length()]; // the right edge of the longest sub palindrome string int right = -1; // the center of the longest sub palindrome string int id = -1; for (int i = 0; i < newStr.length(); i++) { // define the minimum radius int r = 1; if (i <= right) { r = Math.min(right - i, rad[2 * id - i]); } // try to get a lager radius while (i - r >= 0 && i + r < newStr.length() && newStr.charAt(i - r) == newStr.charAt(i + r)) { r++; } //update the right edge and the center of the longest sub palindrome string if (i + r - 1> right) { right = i + r - 1; id = i; } rad[i] = r; } return rad; }
首先。Manacher演算法提供了一個巧妙解決長度為奇數與長度為偶數的不同迴文辦法。在每一個字元見插入一個原字串未出現過的特殊字元,普通情況下用“#”。
這樣無論是aba類型的迴文還是abba類型的迴文。插入特殊字元之後,#a#b#a#和#a#b#b#a#的長度肯定是奇數,這樣就攻克了上面的問題。
Manacher演算法引入一個輔助數組來記錄以每一個字元為中心的最長迴文串的資訊,Rad[i]記錄的是以字元str[i]為中心的最長迴文串。當以str[i]為中心,這個最長迴文串向兩邊延伸Rad[i]個字元。
原串:abbac
新串:#a#b#b#a#c#
輔助數組:12 1 2 5 2 1 2 1 2 1
那麼Manacher演算法是怎麼計算輔助數組Rad的呢?
我們從左往右依次計算Rad[i],當計算Rad[i]時,Rad[j](0<=j<i)已經計算完成。
我們如果整型right為當前最長迴文子串的最右邊緣。而且設當前最長迴文子串的中心點為id,那麼當前指標的位置i就有兩種情況:
第一種:i<=right
那麼找到i相對於中心點的對稱的位置j(2*id-i)。那麼假設Rad[j]<right-i。例如以:
那麼說明以j為中心的迴文串一定在以id為中心的迴文串的內部,且j和i關於位置id對稱,由迴文串的定義可知。一個迴文串反過來還是一個迴文串,所以以i為中心的迴文串的長度至少和以j為中心的迴文串一樣,即Rad[i]>=Rad[j]。由於Rad[j]<right-i。所以說i+Rad[j]<right。由對稱性可知Rad[i]=Rad[j]。
假設Rad[j]>=right-i,由對稱性。說明以i為中心的迴文串可能會延伸到right之外,而大於right的部分我們還沒有進行匹配。所以要從right+1位置開始一個一個進行匹配,直到發生失配,從而更新right和相應的id以及Rad[i]。
另外一種情況:i>right
假設i比right還要大,說明對於中點為i的迴文串還一點都沒有匹配,這個時候,就僅僅能老老實實地一個一個匹配了。匹配完畢後要更新right的位置和相應的id以及Rad[i]。
總結
1. Manacher演算法先巧妙的在全部字元間插入特殊字元,非常好的攻克了迴文字串偶數長度和奇數長度不同處理方法的問題。
2. 事實上Manacher演算法的複雜度不僅僅O(N),可是顯然是介於O(N)和O(N2)之間,是眼下時間複雜度最低的迴文子串演算法。
【LeetCode-面試演算法經典-Java實現】【05-Longest Palindromic Substring(最大迴文字串)】