LeetCode- 5 Longest Palindromic Substring

计算最长回文子串:manacher算法

题目:
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: "babad"

Output: "bab"

Note: "aba" is also a valid answer.

Example:

Input: "cbbd"

Output: "bb"

以下字符串中的空格只是为了看起来方便。

  1. 回文串分为奇数长度的串和偶数长度的串。
    a b c b c len = 5
    a b c c b c len = 6
    为了避免分别考虑这两种情况。manacher算法通过采用预处理的方式。
    预处理:插入特殊字符(原串中不存在的字符)
    原串:a b c b c len = 5
    预处理后:# a # b # c # b # c # Len = 52 +1 = 11
    原串:a b c c b c len = 6
    预处理后:# a # b # c # b # c # Len = 6
    2 +1 = 13
    可以发现预处理后,串的长度都变成了奇数(预处理可以看成在串的每个字符前面加上"#",再在串的末尾加上一个"#’,所以预处理后的长度为Len= 2*len +1,永远都是奇数)

  2. 回文串的表示方法 :采用pos + len的方法。
    maxpos指回文串的中心位置的下标,maxlen指回文串的单向长度
    原串:a b c b c
    pos = 2(注意是原串的下标)
    len = 5(原串中回文串的长度)
    预处理后的串:# a # b # c # b # c #
    maxpos = 5(预处理后的中心位置的下标,注意是下标,从0开始)
    maxlen = 6(预处理后的单向长度,注意是长度,从1开始)
    可以发现:
    pos = maxpos / 2;
    maxlen = len - 1;
    我们就可以根据预处理后求出的回文串对应的原串的长度和下标了

  3. 得到原串的长度和下标后,我们就可以计算原串中回文串的[begin,end)的值
    原串:e a b c b a f
    pos = 3;(pos:原串中回文串中心位置下标)
    len = 5;(len:原串中回文串的长度)
    begin = pos - len / 2 = 3 - 5/2 = 1 ;
    end = pos + (len + 1) / 2 = 3+ 3 = 6;
    得到 [begin, end) 为[1,6)

  4. 下面是manacher算法的主要部分
    对于处理后的串计算一个RL数组(保存以每一个字符为中心位置的回文串的单向长度,这里是向右取长度right-length即RL)
    先把RL数组初始化为1,因为至少包括本身.
    如:# a # b # c # b # c #
    对于a:# a # ,所以RL[1] = 2;
    最终RL[11]={1,2,1,2,1,6,1,2,1,2,1}
    maxpos = 5;
    maxlen = RL[maxpos] = 6;
    此时可计算对应的原串的[begin,end)
    原串:a b c b a
    pos = maxpos / 2 = 2;(原串中回文串的中心位置下标)
    此时可得到对应的原串中[begin,end)的值:
    begin = pos - len / 2 = 2 - 2 = 0;
    end = pos + (len + 1)/2 = 2 +3= 5;
    得到:[0,5)

计算RL数组
从maxpos开始向左右扩展,判断是否相等,若相等,说明单向长度可以增加1,然后在继续判断…

优化manachar算法到O(n)
参考大神的博客
http://blog.tk-xiong.com/archives/1181

class Solution {
public:
    string manacher(string s)
    {
        //预处理
        string str = "#";
        for(int i = 0; i < s.length(); i++)
        {
            str += s.substr(i,1);
            str += "#";
        }
        
        vector <int> RL(str.length(),1);
        
        int MaxRight = 0;       //向右覆盖的回文串的最右边的下标
        int MaxRightPos = 0;    //预处理后回文字符串的中心下标
        
        int MaxLen = 0;         //向右单向长度
        int MaxPos = 0;
        
        for(int i = 0;i < str.length(); i++)
        {
            if(i < MaxRight)
            {
                RL[i] = min(RL[MaxRightPos*2 - i],MaxRight - i);
            }
            while((i-RL[i] >= 0) && (i+RL[i] < str.length()) && str[i+RL[i]] == str[i-RL[i]])
            {
                RL[i] += 1;
            }
        
            //更新回文串最右边下标MaxRight。减1是因为RL[i]包括了i本身占了1位
            if(i+RL[i]-1 > MaxRight)
            {
                MaxRight = i+RL[i]-1;
                MaxRightPos = i;
            }
            
            //更新最长回文串
            if(RL[i] >MaxLen)
            {
                MaxLen = RL[i];
                MaxPos = i;
            }
        }

        int pos = MaxPos / 2;
        int len = MaxLen - 1;
        
        int begin = pos - len / 2;
        string rt = s.substr(begin,len);
        
        return rt;
    }


    string longestPalindrome(string s) 
    {
        return manacher(s);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值