您的位置:首页 > 其它

leetcode题目 寻找最长回文字串

2015-10-02 21:16 344 查看
题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

思路: 常见的解法有三种,时间复杂度分别为O(n3)、O(n2)、O(n)。本文分别实现这三类算法,贴出代码。

解法一: 暴力法,遍历所有可能的字串,判断是否为回文,若是回文记下最长字串长度。以下代码还做了些许优化,已知长度为len的回文字串,则长度小于len的字串直接跳过不做判断。

#include<iostream>
#include<string>
#include<vector>
#include <stdlib.h>
#include <map>
using namespace std;

class Solution {
public:
string longestPalindrome(string s) {
int maxlen=0;
auto start = s.begin();

//长度小于maxlen的字串直接跳过
for (auto i = s.begin(); i != s.end()&&s.end()-i>maxlen; ++i)
{
int len = 0;
for (auto j = s.end(); j > i&&(j-i>maxlen); --j)
{
//若是回文则记下长度,因为之前已经判断当前字串长度大于maxlen
if (IsPalindromic(i, j))
{
maxlen = j - i;
start = i;
}
}
}
return string(start,start+maxlen);
}
bool IsPalindromic(string::iterator begin,string::iterator end)
{
if (end <= begin)
return false;
--end;
while (begin < end &&*begin==*end)
{
begin++;
end--;
}
if (begin >= end)
return true;
else
return false;
}
};

int main()
{
string a;
Solution s;
while (cin >> a)
cout << s.longestPalindrome(a);
}


本地测试结果,有两个测试用例,输出均正确



将以上代码贴到leetcode上,结果却是时间超限。给出的测试用例可以看出很长,O(n3)的解法果然不行。



解法二: 遍历字串,从某个中心开始向两边扩展寻找最长长度的回文串,此解法时间复杂度为O(n2)。注意要区分回文串为奇数长度和偶数长度的情况。另外为了方便边界控制,在向两边延伸时分别使用了string::iterator和string::reverse_iterator.这样向前和向后都可以通过判断是否小于s.rend或者小于s.end来作为边界条件。否则若使用s.begin和s.end作为边界条件则稍复杂。

#include<iostream>
#include<string>
#include<vector>
#include <stdlib.h>
#include <map>
using namespace std;

class Solution {
public:
string longestPalindrome(string s) {
int maxlen = 0;
int lenodd = 0;
int leneven = 0;
int len=0;
auto start = s.begin();
for (auto i = s.begin(); i != s.end(); ++i)
{
//偶数长度的回文串最长半径
leneven = IsPalindromicEven(i, s.end(), s.rend());
//奇数长度的回文串最长半径
lenodd = IsPalindromicOdd(i, s.end(), s.rend());
if (2 * leneven > maxlen)
{
start = i + 1 - leneven;
maxlen = 2 * leneven;
}
if (2 * lenodd-1 > maxlen)
{
start = i + 1 - lenodd;
maxlen = 2 * lenodd - 1;
}
}
return string(start, start + maxlen);
}

int IsPalindromicEven(string::iterator start, string::iterator end, string::reverse_iterator rend)
{
//这里fir指向start,sec指向start后边一位
string::reverse_iterator fir = static_cast<string::reverse_iterator> (start+1);
string::iterator sec = start+1;
int len = 0;
while (fir <rend &&sec < end&&*fir == *sec)
{
++len;
++fir;
++sec;
}
return len;
}

int IsPalindromicOdd(string::iterator start, string::iterator end,string::reverse_iterator rend )
{
//这里fir赋值时需要加1,反向迭代器才能和正向迭代器指向同一个字符
string::reverse_iterator fir = static_cast<string::reverse_iterator> (start + 1);
string::iterator sec = start;
int len = 0;
while (fir <rend &&sec < end&&*fir == *sec)
{
++len;
++fir;
++sec;
}
return len;
}
};

int main()
{
string a;
Solution s;
while (cin >> a)
cout << s.longestPalindrome(a);
}


此种解法通过了leetcode所有用例,但是时间效率却不高,只击败了17%的代码,很明显还有更好的解法。



解法3: Manacher算法,时间复杂度O(n).具体思路介绍网上有很多博客,本文代码则参考了左程云的书籍《IT名企算法与数据结构题目最优解》中关于Manacher算法的介绍。

class Solution {
public:
string longestPalindrome(string s) {
if (s.size() == 0)
return string();
string temp;
temp.push_back('#');
for (auto i = s.begin(); i != s.end(); ++i)
{
temp.push_back(*i);
temp.push_back('#');
}

int *pArr = (int *) malloc(4*temp.size());
memset(pArr, 0, 4*temp.size());
int index = -1;
int pr = -1;
int maxlen = 0;
int start = 0;

for (int i = 0; i != temp.size(); ++i)
{
pArr[i] = pr>i ? min(pArr[2 * index - i], pr - i):1;
while (i + pArr[i]<temp.size() && i - pArr[i]>-1)
{
auto a = temp[i - pArr[i]];
a = temp[i - pArr[i]];
if (temp[i + pArr[i]] == temp[i - pArr[i]])
pArr[i]++;
else
break;
}
if (i + pArr[i] > pr)
{
pr = i + pArr[i];
index = i;
}
if (pArr[i] > maxlen)
{
start = i;
maxlen = max(maxlen, pArr[i]);
}
}
start /= 2;
return string(s, start - ( maxlen - 1) / 2, maxlen-1);
}
};


提交的leetcode后果然速度快了很多,击败了77%的代码。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: