您的位置:首页 > 其它

leetcode 第五题 Longest Palindromic Substring

2017-05-09 15:26 302 查看
        哈哈~我又来啦~

        我最近看完了啊哈磊写的《啊哈!算法》,让我这个小白看懂了里面的基本算法,顺便熟悉回顾了一下数据结构的内容,忽然发现数据结构对于算法的实现复杂度是多么的重要啊。这本书写的真的是很不错,就算是没有任何的基础也可以看得懂,推荐像我一样的小白使用。看完这本书之后我留有一个问题,就是在讲图的割点算法的时候,231页中说“遍历到5号顶点时......对于6号顶点来说就是记录1......”,但是在下面的图的解释low数组中low[6]的值为什么是3而不是1??这里我实在是没有看懂啊,还有,low[5]的值为什么是3啊,5的父亲不是2嘛,2的时间戳是3,不是说low数组记录的是每个顶点在不经过父顶点时能够回到的最小“时间戳”吗?这里我没有看懂。。。有没有看懂的大神知道我一下啊,谢谢!

       我的leetcode已经做到第五题了,这个题的一种新的算法叫Manacher,上周开始接触的时候真的是云里雾里的啊,现在我看懂这个算法啦!不得不说,人是有多聪明才能想出这么个算法,而且还只遍历字符串长度!!我服,这算法牛啊......我能想到的也就是暴力枚举子串判断其是否是最长的回文子串了,但是有一种动态规划的枚举方法会好一点,时间复杂度是O(n^2)。代码如下:

class Solution{
public:
string longestPalindrome(string s){
const int len = s.size();
if(len <= 1)
return s;
bool dp[len][len]; //dp[i][j]表示s[i..j]是否是回文
memset(dp, 0, sizeof(dp));
int resLeft = 0, resRight = 0;
dp[0][0] = true;
for(int i = 1; i < len; i++){
dp[i][i] = true;
dp[i][i-1] = true; //这个初始化容易忽略,当k=2时要用到
}
for(int k = 2; k <= len; k++){ //枚举字串长度
for(int i = 0; i <= len - k; i++){ //枚举子串起始位置
if(s[i] == s[i+k-1] && dp[i+1][i+k-2]){
dp[i][i+k-1] = true;
if(resRight - resLeft < k){
resLeft = i;
resRight = i+k-1;
}
}
}
}
return s.substr(resLeft, resRight-resLeft+1);
}
};        下面就是一种新的算法——Manacher,时间复杂度为O(n),它的基本原理就见这个博客吧:Manacher算法基本原理
class Solution{
public:
string longestPalindrome(string s){
const int len = s.size();
if(len <= 1)
return s;
//Manacher算法,O(n)
string str = preProcess(s);
int n = str.size(), id = 0, mx = 0;
vector<int>p(n,0);
for(int i = 1; i < n-1; i++){
p[i] = mx > i ? min(p[2*id-i], mx-i) : 1;
while(str[i+p[i]] == str[i-p[i]])
p[i]++;
if(i + p[i] > mx){
mx = i + p[i];
id = i;
}
}
//遍历p,寻找最大回文长度
int maxLen = 0, index = 0;
for(int i = 1; i < n-1; i++){
if(p[i] > maxLen){
maxLen = p[i];
index = i;
}
}
return s.substr((index - maxLen)/2, maxLen-1);
}
//预处理字符串
string preProcess(const string &s){
int n = s.size();
string res;
res.push_back('$'); //把$放到字符串头部
res.push_back('#'); //以#作为原来字符串中每个字符的间隔
for(int i = 0; i < n; i++){
res.push_back(s[i]);
res.push_back('#');
}
res.push_back('^'); //以^作为字符串的结尾
return res;
}
};        后来仔细想了想为什么会出现上面的算法,是因为回文字符串的长度有奇数和偶数之分,而加上一些间隔符之后,新的字符串中所有的回文字符串的长度都是奇数。下面就是分奇偶的代码,时间复杂度是O(n^2):
class Solution {
public:
string longestPalindrome(string s) {
const int len = s.size();
if(len <= 1)return s;
int start, maxLen = 0;
for(int i = 1; i < len; i++){
//寻找以i-1,i为中点偶数长度的回文
int low = i-1, high = i;
while(low >= 0 && high < len && s[low] == s[high]){
low--;
high++;
}
if(high - low - 1 > maxLen){
maxLen = high - low -1;
start = low + 1;
}

//寻找以i为中心的奇数长度的回文
low = i- 1; high = i + 1;
while(low >= 0 && high < len && s[low] == s[high]) {
low--;
high++;
}
if(high - low - 1 > maxLen) {
maxLen = high - low -1;
start = low + 1;
}
}
return s.substr(start, maxLen);
}
};         我又学习了一种新的算法,开心(*@ο@*) 哇~就是不知道找我这个速度啥时候才能做完leetcode,呜呜。。。大神们我是不是要将所有的题先过一遍而不是每一道题都细抠一下啊,望给点意见~下次见~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: