最长回文子串python四种方法

动态规划解法:时间复杂度 o(n2),空间复杂度O(n2)
dp[i][j]表示当s[i:j]子串是否是回文串。
当j-i<=1时,如果s[i] == s[j]则表示s[i:j]为回文串,dp[i][j]=1
当j-i > 1时,则判断 s[i]、s[j]是否相等以及dp[i+1][ j-1]是否为true,即s[i+1:j-1]是否为回文串,如果为真,则dp[i][j] = true

#最长回文子串
def longestPalindrome(s):
    dp=[[0 for i in range(len(s))] for j in range(len(s))]
    logestSubStr = "" # 存储最长回文子串 
    logestLen = 0 # 最长回文子串的长度 
    for j in range(len(s)):
        for i in range(j+1):
            if j-i<=1:
                if s[i]==s[j]:
                    dp[i][j]=1
                    if logestLen<j-i+1:
                        logestLen=j-i+1
                        logestSubStr=s[i:j+1]
            elif j-i>1:
                if s[i]==s[j] and dp[i+1][j-1]==1:
                    dp[i][j]=1
                    if logestLen<j-i+1:
                        logestLen=j-i+1
                        logestSubStr=s[i:j+1]
    return logestLen
while True:
    try:
        arr=input()
        lent=longestPalindrome(arr)
        print(lent)
    except:
        break

法二:另一种是(时间复杂度为O(n):
理论支持:每当增加一个新的字母,最大回文串的长度只能增加1或者2,不可能增加更多,并且,新的最大回文串必然要包含这个字母
证明:如果新增了一个字母,最大回文串的长度增加了3,这是不可能的,例如:abcdefgfedcba,当增加到最后的b或者a时,是不可能增加3个长度的,因为每增加一个字母,前面必然已经存在一个回文子串,且长度比新回文串串小1或者小2.
所以,从头到尾扫描字符串,每增加一个新的字符,判断以这个字符结尾,且长度为maxLen+1或者maxLen+2的子串是否为回文,如果是,更新最大回文子串

def longestPalindrome(s):
    if s==s[::-1]:
    	return len(s)
    maxLen=0
    for i in range(len(s)):
        if i-maxLen>=1 and s[i-maxLen-1:i+1]==s[i-maxLen-1:i+1][::-1]:
            maxLen+=2
            continue
        if i-maxLen>=0 and s[i-maxLen:i+1]==s[i-maxLen:i+1][::-1]:
            maxLen+=1
    return maxLen
while True:
    try:
        a=input()
        if a:
            print(longestPalindrome(a))
    except:
        break

法三:根据回文串对称性,分为奇数长度回文串和偶数长度回文串

def match(s):
    res=0
    for i in range(len(s)-1):
        if s[i]==s[i+1]:#奇数长度
            first=i
            end=i+1
            while first>=0 and end<len(s) and s[first]==s[end]:
            #满足条件就向两边扩展
                first-=1
                end+=1
            res=max(res,end-first-1)
        elif s[i-1]==s[i+1]:#偶数长度
            first=i-1
            end=i+1
            while first>=0 and end<len(s) and s[first]==s[end]:
                first-=1
                end+=1
            res=max(res,end-first-1)
    return res
match('abba')

法四:Manacher算法
首先对字符串做一个预处理,在所有的空隙位置(包括首尾)插入同样的符号,要求这个符号是不会在原串中出现的。这样会使得所有的串都是奇数长度的。插入同样的符号且首尾都有,不影响回文性。
Manacher算法用一个辅助数组Len[i]表示以字符T[i]为中心的最长回文字串的最右字符到T[i]的长度,即以T[i]为中心的最长回文串半径,比如以T[i]为中心的最长回文字串是T[l,r],那么Len[i]=r-i+1。
Len数组有一个性质,那就是Len[i]-1就是该回文子串在原字符串S中的长度,至于证明,首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*Len[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有Len[i]个分隔符,剩下Len[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为Len[i]-1。 有了这个性质,那么原问题就转化为求所有的Len[i]。

设mxright为之前计算中最长回文子串的右端点的最大值,并且设取得这个最大值的回文串的对称点位置为mxid,分两种情况:
一:i<=mxright:
找出i关于mxid的对称点j,j=2*mxid-i
如果len[j]<mxright-i,则以j为中心的回文串一定全部在以mxid为中心的回文串中,则以I为中心的回文串半径最低为len[j],即len[i]>=len[j],
如果len[j]>=mxright-i,则以i为中心的回文串可能会延伸到mxright之外,而大于mxright的部分我们还没有进行匹配,所以要从mxright+1位置开始一个一个进行匹配,直到发生失配,从而更新mxright和对应的mxid以及Len[i]。

二:i>mxright
因为mxright右边字符串还没开始匹配,所以以i为中心从左右两边开始匹配,然后更新mxid和mxright位置

def match(s):
    s='#'+'#'.join(s)+'#'
    p=[0]*len(s)
    mx=0
    mid=0
    mxlen=0
    for i in range(len(s)):
    #i在已经匹配过的最长回文子串的右边,以i为中心的回文串最短半径为min(p[j],mx-i),因为p[j]长度可能超过当前回文子串右边界
        if i<mx:
            p[i]=min(p[2*mid-i],mx-i)
        
        else:
            p[i]=1
           #边界条件
        while i+p[i]<len(s) and i-p[i]>=0 and s[i-p[i]]==s[i+p[i]]:
            p[i]+=1
        if p[i]+i-1>mx:
            mid=i
            mx=p[i]-i+1
        mxlen=max(p[i],mxlen)
    return mxlen-1
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值