iT邦幫忙

0

leetcode with python:5. Longest Palindromic Substring

  • 分享至 

  • xImage
  •  

題目:

Given a string s, return the longest palindromic substring in s.

給定一個字串,找出其中最長對稱子字串

這題我的解法偏暴力解

class Solution:
    def longestPalindrome(self, s: str) -> str:
        record1=[]
        record2=[]
        
        for i in range(len(s)): #紀錄最小奇數型對稱字串
            record1.append(i)

        for i in range(len(s)-1): #紀錄最小偶數型對稱字串
            if s[i]==s[i+1]:
                record2.append(i)
                
        i1=1
        i2=2
        while True:
            temp=[]
            i1=i1+2
            for i in record1:
                if i-1>=0 and i+i1-2<len(s) and s[i-1]==s[i+i1-2]:
                    temp.append(i-1)
            if temp==[]:
                i1=i1-2
                break
            else:
                record1=temp
                
        while True:
            temp=[]
            i2=i2+2
            for i in record2:
                if i-1>=0 and i+i2-2<len(s) and s[i-1]==s[i+i2-2]:
                    temp.append(i-1)
            if temp==[]:
                i2=i2-2
                break
            else:
                record2=temp
                
        if record2==[]: #防止連最小偶數型對稱字串都沒有
            return s[record1[0]:record1[0]+i1]
        else:
            if i1>i2:
                return s[record1[0]:record1[0]+i1]
            else:
                return s[record2[0]:record2[0]+i2]

對稱字串有分奇數型跟偶數型,如aba及abba
我分兩種可能去找

用兩個項目來記錄:紀錄對稱字串頭index的record,跟紀錄對稱字串長度的i
每次都比較record內字串兩側是否相等,若相等就放入record,否則排除
接著i+2進入下一次擴張
一旦發現這次擴張導致record變為空陣列
即表示上一次擴張紀錄的字串為此型態最長的對稱字串
record復原為上一次擴張的樣貌,i也-2

最後比較兩種型態的最長字串何者較長
回傳該型態record中的其中一筆字串即可
最後執行時間1632ms(faster than 46.04%)

講了老半天結果是個快超時的解XD

討論區有另一種想法的解
看起來簡潔了些

class Solution:
    def longestPalindrome(self, s: str) -> str:        
        lenres=0
        res=""
                       
        for i in range(len(s)):
            l=i
            r=i
            while l>=0 and r<len(s) and s[r]==s[l]:
                if (r-l+1)>lenres:
                    lenres=r-l+1
                    res=s[l:r+1]
                l=l-1
                r=r+1
            
        for i in range(len(s)):
            l=i
            r=i+1
            while l>=0 and r<len(s) and s[r]==s[l]:
                if (r-l+1)>lenres:
                    lenres=r-l+1
                    res=s[l:r+1]
                l=l-1
                r=r+1
                
        return res

考慮奇偶兩種可能對每個字元下去做擴張
若有發現更長的字串就覆蓋原先紀錄的值
最後回傳紀錄值
最後執行時間1114ms(faster than 75.24%)

快了些,但還不夠快
接下來就是今天的主角,Manacher's Algorithm(馬拉車演算法)
專門用來找一個字串裡的最長回文的演算法(一直打最長對稱子字串好累,改稱回文)
直接用這題來講解它的實作方法應該會比較清楚

class Solution:
    def longestPalindrome(self, s: str) -> str:
        newstr=""
        for i in s:
            newstr=newstr+"*"+i
        newstr="$"+newstr+"*%"
        
        record=[0]*len(newstr)
        
        center=0
        right=0
        for i in range(1,len(newstr)-1):
            mirror=2*center-i
            
            if i < right:
                record[i]=min(right-i,record[mirror])
                
            while newstr[i+record[i]+1]==newstr[i-record[i]-1]:
                record[i]=record[i]+1
                
            if i+record[i]>right:
                center=i
                right=i+record[i]
                
        anslen=max(record)
        ansindex =record.index(anslen)
        return newstr[ansindex-anslen:ansindex+anslen].replace('*', '')

首先先對字串做特殊處理,在每個字元兩側都放有" * "
這樣我們就能一次顧慮到兩種型態的回文下去做擴張
接著在處理後字串兩側放入奇異且相異的兩個符號("$","%")
這樣我們就不用顧慮到字串邊際(這兩個符號會擋下來)

我們有個陣列record紀錄以每個字元到以其為中心的最長回文右邊界的距離
中心(center)和右邊界(right)都先設0,之後會用到
對稱點(mirror)是指當前位置以center為中心的對稱點

這邊講一個重要的性質,以字串a(b)a{c}a[b]ac為例(無視括號,只是做記號)
我們知道以(b)為中心的最長回文長度是3
而以{c}為中心的最長回文長度是7
透過上行給出的信息,我們可以知道[b]為中心的最長回文長度至少>3
為什麼?因為我們知道{c}的左右兩邊的三個字元長得一模一樣
雖然我們知道[b]為中心的最長回文長度是5
但我們至少能從長度3開始擴張

馬拉車演算法就是利用這個性質來提升整個程式的效率
所以上述的center和right就是用來記錄
如上面{c}中心回文這樣能成為幫忙推算最長回文的存在

理論講完終於可以進入過程了
當一個字元發現自己身在以center為中心的最長回文(以下稱c回文)範圍中
我們便可以從record繼承當初在其對稱點已經紀錄的數據開始擴張
但要記得繼承的數據不能超出c回文的右邊界
因為再外就是盲區,是不確定的

而當擴張完發現該字元最長回文的右邊界超過c回文的右邊界
它就會變成我們的新center,其右邊界也會成為新的right
畢竟我們一定是想拿能預測更多字元最大回文長度,涵蓋範圍更多的回文當我們的c回文

結束後只要透過record找出最長回文
去掉" * "後回傳即可

最後執行時間188ms(faster than 97.48%)

本題學習到的重點:馬拉車演算法(Manacher's Algorithm)
那我們下題見


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言