最长回文子串(马拉车算法)

最长回文子串,即正反读起来都一样,例如“ababa”。Manacher Algorithm算法利用了回文的重复特性,让时间复杂度降为了O(n)。
马拉车算法详解:

  1. 改造字符串,在中心扩展法中,要求区分字符串长度为奇数或偶数的情况,我们这里对字符串进行简单的改造,让字符串变成奇数便于处理。例:“ababad” ---->“a#b#a#b#a#d”。为了在检测回文子串的时候避免判断越界,我们可以在开头和结束添加两个不同的字符---->"$a#b#a#b#a#d^"(也不可不做这一步,如果不在开头和结束添加两个不同的字符的话,就需要在检测回文子串的时候检测开头和结尾)
  2. 为了能够充分利用回文子串的重复性质,我们可以将字符串中每个字符的回文半径存如一个数组P中
  3. 这样就将问题转换为了求数组P中的最大值所在的下标和值。但在实际操作中我们并不这样做,是动态的寻着数组P的最大值,即一边写入一边检测,而不是等数组P填充完毕后再去寻找数组P的最大值。因为字符串"a#a"和"#a#“虽然第一个字符串的”#“和第二个字符串的"a"的回文半径是相同的,但是,显而易见,在去掉”#“后 一个是"aa”,一个是"a"。
  4. 关于如何填充数组P
    先设置两个变量,马拉车算法就是在动态的更新这两个变量。
    center:是最大回文子串的中心位置
    mx_right:是最大回文子串所能覆盖的最右端的位置。
    共分为两种情况:
    当 i < mx_right时:即i位于目前的最大回文子串max_str中。那么我们根据回文的重复特性,取检查j = 2 * center - i(j为i以center为中心的对称点)。如果P[j] < mx_right - i 说明以j为中心的回文子串依然位于目前的最大回文子串max_str,很具重复特性可以知道此时P[i] == p[j]。如果P[j] >= mx_right - i,此时只能保证到i的回文子串到mx_right这位置是依然满足的,这里别无他法,只能暴力取匹配mx_right外面的,直到失配,然后在根据大小判断是否更新最大回文子串max_str
    当i >= mx_right时:此时不能充分利用回文子串的重复特性,只能暴力匹配。
def longestPainromedon(s:str):

    if len(s) < 1:
        return s
    new_str = '$' + '#'.join(s) + '^'		#避免判断越界
    center = 1					#最长回文子串中心位置
    mx_right = 1				#最长回文子串最右面覆盖的位置
    max_str = new_str[1]			#最长回文子串
    p = [0]*len(new_str)			#回文子串半径表列
    for i in range(1, len(new_str)-1):

        j = 2*center - i
        if i < mx_right:
            if p[j] < mx_right - i:
                p[i] = p[j]
            else:
                k = mx_right+1
                while new_str[k] == new_str[2*i - k]:
                    k += 1
                k -= 1
                p[i] =  k - i
                cur_str = new_str[i - p[i]:i + p[i] + 1].replace('#', '')
                if len(cur_str) > len(max_str):
                    max_str = cur_str
                    center, mx_right = i, k
        else:
            k = 0
            while new_str[i - k - 1] == new_str[i + k + 1]:
                k += 1
            p[i] = k
            cur_str = new_str[i - p[i]:i+p[i] + 1].replace('#', '')
            if len(cur_str) > len(max_str):
                max_str = cur_str
                center, mx_right = i, k + i

    return max_str

代码如上,此代码时完全照算法来的,其他的博客的代码我想应该是有所优化,导致我一开始有点懵。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一个字符串 s,找到 s 中最长的回文子串。 回文串是指正着读和反着读都一样的字符串。 示例: 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。 输入: "cbbd" 输出: "bb" 解题思路: 1.暴力法 暴力法是最简单直接的方法,遍历所有子串,判断是否为回文串,记录最长的回文串。 时间复杂度:O(n^3) 2.中心扩展法 中心扩展法是基于回文串的对称性质,从中心向两边扩展,判断是否为回文串,记录最长的回文串。 时间复杂度:O(n^2) 3.动态规划法 动态规划法是基于回文串的子串也是回文串的性质,利用状态转移方程,记录最长的回文串。 时间复杂度:O(n^2) 4.马拉车算法 马拉车算法是基于回文串的对称性质和回文串的最大半径,利用中心扩展法的思想,减少重复计算,记录最长的回文串。 时间复杂度:O(n) 参考代码: 1.暴力法 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string res = ""; for (int i = ; i < n; i++) { for (int j = i; j < n; j++) { int len = j - i + 1; if (len > res.size() && isPalindrome(s, i, j)) { res = s.substr(i, len); } } } return res; } bool isPalindrome(string s, int left, int right) { while (left < right) { if (s[left] != s[right]) return false; left++; right--; } return true; } }; 2.中心扩展法 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string res = ""; for (int i = ; i < n; i++) { string s1 = palindrome(s, i, i); string s2 = palindrome(s, i, i + 1); res = res.size() > s1.size() ? res : s1; res = res.size() > s2.size() ? res : s2; } return res; } string palindrome(string s, int left, int right) { while (left >= && right < s.size() && s[left] == s[right]) { left--; right++; } return s.substr(left + 1, right - left - 1); } }; 3.动态规划法 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; vector<vector<bool>> dp(n, vector<bool>(n, false)); int start = , maxLen = 1; for (int i = ; i < n; i++) { dp[i][i] = true; } for (int j = 1; j < n; j++) { for (int i = ; i < j; i++) { if (s[i] != s[j]) { dp[i][j] = false; } else { if (j - i < 3) { dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } } if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; start = i; } } } return s.substr(start, maxLen); } }; 4.马拉车算法 class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) return s; string t = "#"; for (int i = ; i < n; i++) { t += s[i]; t += "#"; } n = t.size(); vector<int> p(n, ); int center = , maxRight = , start = , maxLen = 1; for (int i = ; i < n; i++) { if (i < maxRight) { int mirror = 2 * center - i; p[i] = min(maxRight - i, p[mirror]); } int left = i - (p[i] + 1), right = i + (p[i] + 1); while (left >= && right < n && t[left] == t[right]) { p[i]++; left--; right++; } if (i + p[i] > maxRight) { center = i; maxRight = i + p[i]; } if (p[i] > maxLen) { maxLen = p[i]; start = (i - maxLen) / 2; } } return s.substr(start, maxLen); } };

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值