题目链接: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