最长回文子串/子序列问题(Python最全整理)

将近期刷LeetCode题目时的一些心得与总结与大家分享一下。
这次,将集中整理最长回文子串、最长回文子序列的问题。

解决问题

给定一个字符串,要求求出这个字符串中的最长的回文串子串。此处以最长回文子串为例进行讲解,最后给出最长回文子序列的求解思路与方法。
注:最长回文子串与最长回文子序列是不同的,回文子串要求所求的字符串连续,最长回文子序列,则不要求。因此,也导致两者求解思路的不同。
例如,对‘abcda’, 最长回文字符串长度为1,任意一个字符均可;而最长回文子序列长度为3,即a(b,c,d)a。(b,c,d)表示任选一个字符

求解思路一:采用暴力求解

自前至后求解所有子串,并判断相应的子串是否为回文子串,找出其中最长的那个。

求每一个子串时间复杂度O( n 2 n^2 ), 判断子串是不是回文O(n),两者是相乘关系,所以时间复杂度为O( n 3 n^3 )。

求解思路二:动态规划

对于字符串问题,常用的一种思路是动态规划的方式。主要思路来源于对于思路一的优化。在思路一中,是将所有的字符串进行了组合,再判断。一种可以想到的简化思路时,在组合字符串的过程中,动态处理,只保留可以组成回文字符串的,而不必将每种组合都进行处理。从而节省时间,也节省了最后判断是否为回文子串的过程。
引入DP[i][j],表示第i个字符至第j个字符组成的子串是否为回文子串。
则状态转移方程为:

D P [ i ] [ j ] = { 1 , if  i = j s [ i ] = s [ j ] , if  i + 1 = j ,字符串长度为2的情况 s [ i ] = s [ j ] D P [ i + 1 ] [ j 1 ] , else DP[i][j]= \begin{cases} 1, & \text {if $i$=$j$} \\ s[i] = s[j], & \text{if $i+1$=$j$,字符串长度为2的情况} \\ s[i] = s[j] \bigwedge DP[i+1][j-1], & \text{else} \end{cases}

注:对于长度为2的情况,即i+1=j时,实质上不存在DP[i+1][j-1]。此时j- 1 > i+1,所以需单独处理一下。其实,也可以通过将数组初始化为1来求解,这样不需要判断(详细见下方源码)

在使用动态规划求解时,需保证计算当前数组中的值时,则需要判断相应的遍历规则。
以字符串‘abca’为例:能确定出值的,为下表中已填入数字的部分。

a b c a
a 1
b 1 1
c 1 1 1
a 1 1 1 1

根据上述状态转移方程,我们可以的填充顺序为:
(1,2)->(2,3)->(3,4)->(1,3)->(2,4)->(1,4)
即按照斜线,自对角线开始逐渐向右上角填充;
或者自下而上、自左至右依次填充。即行遍历由下至上,列遍历由左至右的方式。
综上,代码如下:

class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if not s:
            return ""
        n = len(s)
        # 动态规划算法
        dp = [[1 for _ in range(n)] for _ in range(n)]
        sta = 0
        end = 0
        max_l = 1
        for i in range(n - 1, -1 , -1):
            dp[i][i] = 1
            for j in range(i + 1, n):
                dp[i][j] = (s[i] == s[j]) & dp[i + 1][j - 1]
                # 保留最大值,基于上述遍历规则,此处保留的为最右端的最长子串;如果求最左端的,将下述的<变为<=即可
                if dp[i][j] and max_l < j - i + 1:
                    max_l = j - i + 1
                    sta = i                

        return s[sta:max_l + sta]

求解思路三:Manacher算法(马拉车算法)

这是网上提供的线性时间算法。
以字符串‘abca’为例。
S1:首先用特定字符,比如"#",去填充原来的字符串s,从而将长度n变为2n+1。
“#a#b#c#a#”
S2:引入数组A, A[i]表示以新字符串中第i个字符为中心的最长回文子串的长度(只须记录往一侧扩展的值即可,计算方便)
A[0]: 1
A[1]: 3 “#a#”

S3:找出数组A中最大的值,并从中心点截取前后max(Len)-1长度,再去掉“#"即为最后所要求的字符串。

扫描二维码关注公众号,回复: 5108913 查看本文章
class Solution:
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        if not s:
            return ""
        n = len(s)
        temp_s = ['#']
        for i in range(n):
            temp_s.append(s[i])
            temp_s.append('#')
        out_len = []
        for i in range(2 * n + 1):
            temp_len = 1
            try:
                while temp_s[i - temp_len] == temp_s[i + temp_len]:
                    temp_len += 1
            except IndexError:
                pass
            out_len.append(temp_len)
        max_len = max(out_len)
        max_len_index = out_len.index(max_len)
        out = temp_s[max_len_index - max_len + 1:max_len_index + max_len]
        res = ""
        for each in out:
            if each != '#':
                res += each
        
        return res

扩展问题:求回文子序列的个数

注:未考虑子序列的重复问题

如果一个字符串为回文子序列,则去掉前后各1个字符后,仍为回文子序列。

求解思路:基于动态规划

引入DP[i][j],表示第i个字符至第j个字符组成的子串中回文子串的数量。
此时的状态转移方程:
D P [ i ] [ j ] = { 1 , if  i = j D P [ i + 1 ] [ j 1 ] + 1 , if s[i] = s[j] D P [ i ] [ j 1 ] + D P [ i + 1 ] [ j ] D P [ i + 1 ] [ j 1 ] , if s[i] != s[j],集合求并 DP[i][j]= \begin{cases} 1, &amp; \text {if $i$=$j$} \\ DP[i+1][j-1] + 1, &amp; \text{if s[i] = s[j]} \\ DP[i][j-1] + DP[i+1][j] - DP[i+1][j-1], &amp; \text{if s[i] != s[j],集合求并} \end{cases}
代码中,需要注意,DP要初始化为0。

最长回文子序列问题

求解思路:基于动态规划

引入DP[i][j],表示第i个字符至第j个字符组成的子串中最长回文子序列的长度。
此时的状态转移方程:
D P [ i ] [ j ] = { 1 , if  i = j D P [ i + 1 ] [ j 1 ] + 2 , if s[i] = s[j] m a x ( D P [ i ] [ j 1 ] + D P [ i + 1 ] [ j ] ) , if s[i] != s[j] DP[i][j]= \begin{cases} 1, &amp; \text {if $i$=$j$} \\ DP[i+1][j-1] + 2, &amp; \text{if s[i] = s[j]} \\ max(DP[i][j-1] + DP[i+1][j]), &amp; \text{if s[i] != s[j]} \end{cases}

参考文章

最长回文字串
每天一道LeetCode-----最长回文子串/序列,从头开始的最长回文子串长度

猜你喜欢

转载自blog.csdn.net/weixin_42503575/article/details/86678402
今日推荐