(Manacher Algorithm, 中心拓展法,动态规划) leetcode 5. 最长回文串

解法一:中心拓展法。从下标为0开始遍历,将每个元素当作回文串中心,向两边拓展。

1)以这个字符为中心的回文串的长度(奇数串);

2)以这个字符和下个字符为中心的回文串的长度(偶数串)。

注意:既要统计回文串为奇数时,又要统计回文串为偶数时。当 s[left]!=s[right] 时,left多减了1,right多加了1,所以在计算回文串开头时要把left+1,长度要是(right-1)-(left+1)-1 = right - left -1

class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.size();
        int maxlen = 1;
        int start = 0;

            //aba
            for(int i=0; i<len; i++){
                int j = i-1;
                int k = i+1;
                while(j>=0 && k<len && s[j]==s[k]){
                    if(k-j+1 > maxlen){
                        maxlen = k-j+1;
                        start = j;
                    }
                    j--;
                    k++;
                }
            }
            //abba
            for(int i=0; i<len; i++){
                int j = i;
                int k = i+1;
                while(j>=0 && k<len && s[j]==s[k]){
                    if(k-j+1 > maxlen){
                        maxlen = k-j+1;
                        start = j;
                    }
                    j--;
                    k++;
                }
            } 

        return s.substr(start, maxlen);
    }
};
class Solution {public:    string longestPalindrome(string s) {        //这个写法更简便        if(s.size() <2 )            return s;        int maxlen = 0, start=0;        for(int i=0; i<s.size()-1; ++i){            palind(s,i,i,start, maxlen);            palind(s, i, i+1, start, maxlen);        }        return s.substr(start, maxlen);    }        void palind(string s, int left, int right, int& start, int& maxlen){        while(left>=0 && right<s.size() && s[left]==s[right]){            right++;            left--;        }        if(maxlen < right-left-1){            start = left+1;            maxlen = right - left -1;        }    }    };
class Solution {public:    string longestPalindrome(string s) {        int len = s.size();        if(len==0 || len==1)            return s;                string s1="", s2="", p="";        for(int k=0; k<len; ++k){            //这里 k<len 或 k<len-1 都可以            s1 = palind(s, k, k);            if(s1.size() > p.size())                p = s1;                        s2 = palind(s, k, k+1);            if(s2.size() > p.size())                p = s2;        }        return p;    }    string palind(string s, int i, int j){        //以i和j为两端的回文串长度        while(i>=0 && j<s.size() && s[i] == s[j] ){            i--;            j++;        }        return s.substr(i+1, j-i-1);     }};

我把重复的代码写成一个函数调用之后 更慢了==

解法二:Manacher Algorithm(马拉车算法)是解决在一个字符串中寻找最长回文串的O(n)算法。实在是不好理解。

参考视频:https://www.youtube.com/watch?v=SV1ZaKCozS4

参考链接:https://zhuanlan.zhihu.com/p/62351445?utm_source=wechat_session&utm_medium=social&utm_oi=544807589276360704

思路:

1. 调整字符串

因为回文子串有两种可能"aba"和"bb",如果直接处理需要判断子串中心是否有字符。而马拉车算法先为字符串填充无效字符,例如"#"。这样上述字符串就变成"#a#b#a#"和"#b#b#"。这样无论原字符串怎样,新生成的字符串都是长度为奇数,中心有字符。

2. 判断字符半径

这里先引入一些概念和变量。

字符半径:就是以该字符为中心可以形成的最大回文字符串的半径。比如"#a#b#a#"的半径为3。

节点 i :被遍历节点i。

节点maxR : 容纳节点i最大回文子串所覆盖的最大位置。

节点pos : 容纳节点i最大回文子串的中心位置。

节点j : 以pos为中心,节点i的对称位置。

数组R : 记录所有节点为中心的最大回文半径,如"#a#b#a#" R[3] 的最大回文半径为4,要在字符半径的基础上加上本身的长度1 。

之后节点i从左至右遍历字符串,首先预估节点i最小半径,不断扩大搜索范围以确定最终半径。数组R记录下来。最终有了全部回文子串的中心和半径就能确定算法就可以解决。

3. 如何确定节点i的最小半径

如果所有节点i的半径都从0开始枚举,算法复杂度太高。因为在遍历节点i之前,数组R已经记录了过去节点的回文信息。通过maxR和pos记录容纳节点i最大回文子串信息。因为回文子串的左右必然对称,可以估计节点i的半径最小在maxR-i 和其对称节点 j = pos*2 -1 半径之间。如图:

公式:

当节点i探索范围超过maxR,则替换maxR和pos。以此遍历完整个字符串后,选择最大子串,保留在奇数位原字符串字符即可。

class Solution {
public:
    string manacher(string& x){
        string a = "#";
        for(char v : x){
            a.push_back(v);
            a.append("#");
            // #c#b#b#d#   #b#a#b#a#d#
        }
        int pos = 0;   //容纳节点i最大回文子串的中心位置
        int maxR = 0;  //容纳节点i最大回文子串所覆盖的最大位置
        // R 记录所有节点为中心的最大回文串的半径
        vector<int> R(a.size(), 0);
        for(int i=0; i<a.size(); ++i){
            // 2*pos-i = mirror(i)
            //节点i未超过最大回文串的边界时,取min(mirror[i], 最大回文串的边界),否则为0
            R[i] = maxR>i ? min(maxR-i, R[2*pos-i]) : 0;
            //从i+R[i] 和 i-R[i]之外开始遍历看是否相同
            while(R[i]+i<a.size() && i>=R[i] && a[i-R[i]] == a[i+R[i]])
                R[i]++;
            if(R[i] + i > maxR){
                maxR = R[i]+i;
                pos = i;
            }
        }

        int sub[] = {0, 0};  //sub储存{回文串中心,半径长度}
        for(int i=0; i<a.size(); ++i){
            if(R[i] > sub[1]){
                sub[0] = i;
                sub[1] = R[i];
            }
        }

        string sub2 = "";
        for(int i=sub[0]-sub[1]+1; i<=sub[0]+sub[1]-1; ++i){
            if(i%2 == 1)
                //取奇数位
                sub2 += a[i];
        }
        return sub2;
    }

    string longestPalindrome(string s) {
        return manacher(s);
    }
};

原文地址:https://www.cnblogs.com/Bella2017/p/11258444.html

时间: 2024-08-28 16:25:51

(Manacher Algorithm, 中心拓展法,动态规划) leetcode 5. 最长回文串的相关文章

LeetCode 5 迅速判断回文串的曼切斯特算法

题意 Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000. Link: https://leetcode.com/problems/longest-palindromic-substring/ 翻译 给定一个字符串s,要求它当中的最长回文子串.可以假设s串的长度最大是1000. 样例 Example 1: Input:

Manacher算法 O(n)求最长回文串

最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5158    Accepted Submission(s): 1755 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组

hdu 3068 最长回文串 o(n) Manacher 算法

最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10596    Accepted Submission(s): 3759 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多

hiho#1032 : 最长回文子串 (manacher算法O(n)时间求字符串的最长回文子串 )

#1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:"小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?" 小Ho奇怪的问道:"什么叫做最长回文子串呢?" 小Hi回答道:"一个字符串中连续的一

Manacher算法 最长回文串

Manacher算法O(n) 因为对于偶回文,是需要从虚轴扩充,ab,ba,所以如下: 先把原字符串处理,都加上一个标记符,比如#(特殊字符任何都可以,对于计算结果不会有影响) 1221-->#1#2#2#1# 121-->#1#2#1# 按照处理后的字符串求它的最长回文串长度m,所以原始字符串最长子回文串的长度是m/2 变量: 1:PArra[] 存放回文半径:某个位置能扩充的回文半径的长度,例如 #1#2#2#1#,2位置PArra[3] = 4 2:int PR 能够扫到的最右的回文的位

Hdu 3294 Girls&#39; research (manacher 最长回文串)

题目链接: Hdu 3294  Girls' research 题目描述: 给出一串字符串代表暗码,暗码字符是通过明码循环移位得到的,比如给定b,就有b == a,c == b,d == c,.......,a == z. 问最长回文串所在区间,以及最长回文串所表示的明码. 解题思路: 字符串长度[1,200000],用manacher算法很轻松就搞定了. get√新技能请点击me 1 #include <cstdio> 2 #include <cstring> 3 #includ

HDU 3068-最长回文(Manacher算法O(n)求最长回文串)

题目地址:HDU 3068 关于算法的详解:Manacher算法 #include <stdio.h> #include <math.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <sstream> #include <algorithm> #include <set> #include <queue>

HDU ACM 4513 吉哥系列故事——完美队形II-&gt;求最长回文串(manacher算法)

分析:该題可以通过求最长回文串的方法来解决:求最长回文串使用manacher算法,O(n)时间复杂度. 注意:while(a[i-len[i]]==a[i+len[i]] && a[i-len[i]]<=a[i-len[i]+2])这里多出的判断a[i-len[i]]<=a[i-len[i]+2]即为该題的限制从左到中保证身高不降,因在回文串的计算过程中添加了额外的字符,所以这里是i-len[i]+2而不是i-len[i]+1,以避开添加的字符. #include<ios

【leetcode 简单】 第九十六题 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意: 假设字符串的长度不会超过 1010. 示例 1: 输入: "abccccdd" 输出: 7 解释: 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7. class Solution(object): def longestPalindrome(self, s): &quo