您的位置:首页 > 其它

【LeetCode】#5 Longest Palindromic Substring

2017-02-02 22:26 423 查看
LeetCode 题目,原题链接 https://leetcode.com/problems/longest-palindromic-substring/

问题描述:找到字符串中最长回文子串。

参考:http://articles.leetcode.com/longest-palindromic-substring-part-i/http://articles.leetcode.com/longest-palindromic-substring-part-ii

按照深入程度,解决方法如下。

方法一

猜想:回文就是正着读、反着读结果都是一样的字符串。那么把字符串给反过来,得到一个新字符串,比较原字符串与新字符串的最长相同子串就能得到结果。如
S="fabcba"
S="abcbaf"
相同部分为
"abcba"
,所以
"abcba"
S
中的最长回文子串。

但是,结果就是这么简单吗?这样想当然简单了。考虑,
T="abcdba"
T'="abdcba"
,它们最长相同子串是
"ab"
,但是这东西不是回文。这个个例问题出在了将不同位置上的最长相同子串当做了回文。

解决方法是考虑相同子串的位置,如在
S
S'
自身长度为
size=6
,最长相同子串长度为
len=5
,子串在
S
S'
中的位置分别为
p=1
p'=0
,存在这样的关系
(size-1)-(p+len-1)=p'


#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

string longestPalindromeReverse(const string &s) {
int size = s.size();
string r = s;
std::reverse(r.begin(), r.end());
int longestLength = 1;
int longestIndex = 0;

for (int len = 1; len < size + 1; ++len) {
for (int i = 0; i < size - len + 1; ++i) {
string sub = s.substr(i, len);
int p = i;
int p_prime = r.find(sub);

if (p_prime != string::npos && (size - 1) - (p + len - 1) == p_prime) {
if (longestLength < len) {
longestLength = len;
longestIndex = i;
}
}
}
}

return s.substr(longestIndex, longestLength);
}

int main() {
cout << longestPalindromeReverse("zeusnilemacaronimaisanitratetartinasiaminoracamelinsuez") << endl;

return 0;
}


这个方法的时间复杂度是 O(n2),但是最内层循环有
find
函数,非常耗时间。所以被 LeetCode 以
Time Limit Exceeded
为理由拒绝了。

空间复杂度是 O(n)。

方法二

动态规划的方法,动态规划的套路是用一个多维的数组保存中间结果,在计算时使用中间结果加速计算。所以动态规划的空间复杂度一般比较大。

用一二维数组
table
保存中间结果,
table[i][j]
表示
[i, j]
的子串是否是回文。

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

string longestPalindromeDP(const string &s) {
int longestBegin = 0;
int longestLength = 1;

bool table[1000][1000] = {false};

for (int i = 0; i < s.size(); ++i) {
table[i][i] = true;
}

for (int i = 0; i < s.size() - 1; ++i) {
if (s[i] == s[i + 1]) {
table[i][i + 1] = true;
longestBegin = i;
longestLength = 2;
}
}

for (int len = 3; len < s.size() + 1; ++len) {
for (int i = 0; i < s.size() - len + 1; ++i) {
if (table[i + 1][i + len - 2] == true && s[i] == s[i + len - 1]) {
table[i][i + len - 1] = true;
longestLength = len;
longestBegin = i;
}
}
}

return s.substr(longestBegin, longestLength);
}

int main() {
cout << longestPalindromeDP("zeusnilemacaronimaisanitratetartinasiaminoracamelinsuez") << endl;

return 0;
}


方法三

暴力搜索,找到回文所有可能的中间点,从中间点处向两侧扩展,搜索完所有的可能性,就能找到最长回文子串。

注意,中间点可能是一个字符也有可能是两个字符,如
aba
abba


#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

string expandArroundCenter(const string &s, int begin, int end) {
int length = s.size();
int l = begin - 1, r = end + 1;

while (l >= 0 && r <= length - 1 && s[l] == s[r]) {
--l;
++r;
}

return s.substr(l + 1, r - 1 - l);
}

string longestPalindromeSimple(const string &s) {
int length = s.size();
if (length == 0) return "";
string longest = s.substr(0, 1);
int longestLength = 1;

int i = 0;

for (i = 0; i < length - 1; ++i) {
string p1 = expandArroundCenter(s, i, i);
if (p1.size() > longestLength) {
longestLength = p1.size();
longest = p1;
}
if (s[i] == s[i + 1]) {
string p2 = expandArroundCenter(s, i, i + 1);
if (p2.size() > longestLength) {
longestLength = p2.size();
longest = p2;
}
}
}

return longest;
}

int main() {
cout << longestPalindromeSimple("abacdgfdcaba") << endl;
return 0;
}


方法四

利用回文的对称性缩减计算时间。这个方法叫做
Manacher’s Algorithm
。维基百科上就是这个方法。

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

string preProcess(const string &s) {
int n = s.size();
if (n == 0) return "^$";
string ret = "^";
for (int i = 0; i < n; ++i) {
ret.append("#").append(s.substr(i, 1));
}
ret.append("#$");

return ret;
}

string longestPalindrome(const string &s) {
string T = preProcess(s);
int n = T.size();
int C = 0, R = 0;
int* P = new int
;

for (int i = 1; i < n - 1; ++i) {
int i_mirror = 2 * C - i;

P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0;

while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) {
++P[i];
}

if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}

int longestLength = 0, longestIndex = 0;
for (int i = 1; i < n - 1; ++i) {
if (P[i] > longestLength) {
longestLength = P[i];
longestIndex = i;
}
}

delete[] P;

return s.substr((longestIndex - 1 - longestLength)/2, P[longestIndex]);
}

int main() {
cout << longestPalindrome("abacdgfdcaba") << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode