最容易想到的辦法是枚舉出全部的子串,而後一一判斷是否爲迴文串,返回最長的迴文子串長度。不用我說,枚舉實現的耗時是咱們沒法忍受的。那麼有沒有高效查找回文子串的方法呢?答案固然是確定的,那就是中心擴展法,選擇一個元素做爲中心,而後向外發散的尋找以該元素爲圓心的最大回文子串。可是又出現了新的問題,迴文子串的長度便可能是奇數,也可能好是偶數,對於長度爲偶數的迴文子串來講是不存在中心元素的。那是否有一種辦法能將奇偶長度的子串歸爲一類,統一使用中心擴展法呢?它就是 manacher 算法,在原字符串中插入特殊字符,例如插入 #後原字符串變成'#3#5#5#3#4#3#2#1#'。如今咱們對新字符串使用中心擴展發便可,中心擴展法獲得的半徑就是子串的長度。python
如今實現思路已經明確了,先轉化字符串'35534321' ----> '#3#5#5#3#4#3#2#1#',而後求出以每一個元素爲中心的最長迴文子串的長度。如下給出 python 實現:算法
#!/usr/bin/python # -*- coding: utf-8 -*- def max_substr(string): s_list = [s for s in string] string = '#' + '#'.join(s_list) + '#' max_length = 0 length = len(string) for index in range(0, length): r_length = get_length(string, index) if max_length < r_length: max_length = r_length return max_length def get_length(string, index): # 循環求出index爲中心的最長迴文字串 length = 0 r_ = len(string) for i in range(1,index+1): if index+i < r_ and string[index-i] == string[index+i]: length += 1 else: break return length if __name__ == "__main__": result = max_substr("35534321") print result
功能已經實現了,通過測試也沒有 bug,可是咱們靜下心來想想,目前的解法是否還有優化空間呢?根據目前的解法,咱們求出了‘35534321‘中每一個元素中心的最大回文子串。當遍歷到'4'時,咱們已經知道目前最長的迴文子串的長度 max_length 是 4,這是咱們求出了以 4 爲中心的最長迴文子串長度是 3,它比 max_length 要小,因此咱們不更新 max_length。換句話說,咱們計算以 4 爲中心的最長迴文字串長度是作了無用功。這就是咱們要優化的地方,既然某個元素的最長的迴文子串長度並無超過 max_length,咱們就沒有必要計算它的最長迴文子串,在遍歷一個新的元素時,咱們要優先判斷以它爲中心的迴文子串的長度是否能超越 max_length,若是不能超過,就繼續遍歷下一個元素。如下是優化後的實現:ide
#!/usr/bin/python # -*- coding: utf-8 -*- def max_substr(string): s_list = [s for s in string] string = '#' + '#'.join(s_list) + '#' max_length = 0 length = len(string) for index in range(0, length): r_length = get_length2(string, index, max_length) if max_length < r_length: max_length = r_length return max_length def get_length2(string, index, max_length): # 基於已知的最長字串求最長字串 # 1.中心+最大半徑超出字符串範圍, return r_ = len(string) if index + max_length > r_: return max_length # 2.沒法超越最大半徑, return l_string = string[index - max_length + 1 : index + 1] r_string = string[index : index + max_length] if l_string != r_string[::-1]: return max_length # 3.計算新的最大半徑 result = max_length for i in range(max_length, r_): if index-i >= 0 and index+i < r_ and string[index-i] == string[index+i]: result += 1 else: break return result - 1 if __name__ == "__main__": result = max_substr("35534321") print result