LeetCode5. Longest Palindromic Substring(最长回文子串:Manacher算法)

来源:互联网 发布:用户购买数据分析 编辑:程序博客网 时间:2024/05/17 20:31

题目链接:https://leetcode.com/problems/longest-palindromic-substring/


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"

解题思路:Manacher算法 O(n)


诚然,最长回文子串问题有很多算法,后缀数组、Manacher算法等,算法原理不再赘述,参考如下博文


最长回文子串:http://blog.csdn.net/kangroger/article/details/37742639


笔者是参考了《ACM国际大学生程序设计竞赛:算法与实现》上提供的void manacher(char str[ ], int len[ ], int n)接口实现的。


最长回文子串manacher算法

【任务】

给定一个字符串S,求出S的最长回文子串。


【接口】

void manacher(char str[ ], int len[ ], int n);

复杂度:O(n)

输入: str 文本串

             n   文本串长度

输出:len[ ] 所有中心的回文串长度


void manacher(char str[],int len[],int n){    len[0] = 1;    for(int i = 1,j = 0; i < (n<<1) - 1;++ i){        int p = i >> 1,q = i - p, r = ((j+1) >> 1) + len[j] - 1;        len[i] = r < q?0:min(r-q+1,len[(j<<1) - i]);        while(p > len[i] - 1 && q + len[i] < n && str[p - len[i]] == str[q+len[i]]) ++len[i];        if(q + len[i] - 1 > r) j = i;    }}



解题思路:充分理解len[0...2n-1]的含义,找到要打印的目标回文串的起始位置和长度即可(分两种情况讨论


len[i]表示中间串tmp中,以tmp[i]为中心的回文子串的半径(不包括#),注意:tmp[i]可能为文本串中的字符,也可能为#!


情况1:abcba   ===> 回文串中心为文本串中的字符

中间串tmp: a # b # c # b # a

len[0..9]    :    1 0 1 0 3 0 1 0 1

    

情况2:abccba  ===> 回文串中心为 #

中间串tmp: a # b # c # c # b # a

len[0..9]    :    1 0 1 0 1 3 1 0 1 0 1


我的做法是找到这两种情况在文本串中分别对应的起始位置pos和长度max_len,分别打印输出即可


(相信思路还是非常清晰的,读者自己实现就可以噻~)


参考代码:

void manacher(char str[],int len[],int n){//接口    len[0] = 1;    for(int i = 1,j = 0; i < (n<<1) - 1;++ i){        int p = i >> 1,q = i - p, r = ((j+1) >> 1) + len[j] - 1;        len[i] = r < q?0:min(r-q+1,len[(j<<1) - i]);        while(p > len[i] - 1 && q + len[i] < n && str[p - len[i]] == str[q+len[i]]) ++len[i];        if(q + len[i] - 1 > r) j = i;    }}class Solution {public:    string longestPalindrome(string s) {        int n = s.size();        int len[2000];        char *str = &s[0];        manacher(str,len,n);//调用接口,得到len[]        string tmp = "";        int pos = 0,max_len = 0;        for(int i = 0;i < (n<<1) - 1; ++ i){            int tmp_len = (i&1)?len[i]<<1:(len[i]<<1)-1; //以‘#’or字符为中心,串长不一样            if(tmp_len > max_len) pos = i,max_len = tmp_len; //pos记录目标串的中心点,max_len表示目标串的串长(不含#)            if(i&1) tmp+="#"; else tmp+=s[i>>1];    //作一个tmp[0..2n-1]的字符串,便于输出        }        if(pos&1){   //找到要打印的串tmp的起始位置pos和打印长度max_len(便于打印输出)          max_len = (len[pos] << 2) - 1;          pos = pos - (len[pos] << 1) + 1;        }        else{           max_len = (len[pos] << 2) - 3;           pos = pos - ((len[pos]-1)<<1);        }        string ans = "";        for(int i = pos,j = 0;j < max_len;++ j,++ i){            if(i&1) continue;            ans+=tmp[i];     //tmp中找到所要打印的字符,链接起来        }        return ans;    }};



最后,总结一下字符串专题


1_______

LIS(最长上升子序列|子串)O(nlogn)

原理就是再作一个新的序列,二分查找upper_bound到大于等于当前元素的位置,替换掉即可;


2______

LCS(最长公共子序列|子串)O(n^2)

只会dp[m][n]的做法,如果子序列没有重复元素,可以转换成LIS求解O(nlogn)

http://blog.csdn.net/u012717411/article/details/47335507


3______

最长不重复子串O(n)

http://blog.csdn.net/u012717411/article/details/53339392


4______

最长回文子串 manacher算法 O(n)

http://blog.csdn.net/u012717411/article/details/53363444


5______

kmp 串匹配 O(n+m)

http://blog.csdn.net/u012717411/article/details/48878627


6_______

字典树(单词查找树)

http://blog.csdn.net/u012717411/article/details/49513053


1 0