Medium!
题目描述:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 长度最长为1000。
示例:
输入: "babad" 输出: "bab" 注意: "aba"也是有效答案
示例:
输入: "cbbd" 输出: "bb"
回文串概念:
“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
回文算法描述:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 int main() 6 { 7 string str; 8 int i,j,l; 9 int flag = 1; 10 11 while (cin >> str) 12 { 13 l = str.length(); 14 for (i = 0,j = l-1; i <= j; i++,j--) 15 { 16 if (str[i] != str[j]) 17 { 18 flag = 0; 19 } 20 } 21 if (flag) cout << "YES" << endl; 22 else cout << "NO" << endl; 23 flag = true; 24 } 25 26 return 0; 27 }
解题思路:
LeetCode中关于回文串的题共有五道,除了这道,其他的四道为
C++参考答案一: 此题还可以用动态规划Dynamic Programming来解,与Palindrome Partitioning II 拆分回文串之二的解法很类似,我们维护一个二维数组dp,其中dp[i][j]表示字符串区间[i, j]是否为回文串,当i = j时,只有一个字符,肯定是回文串,如果i = j + 1,说明是相邻字符,此时需要判断s[i]是否等于s[j],如果i和j不相邻,即i - j >= 2时,除了判断s[i]和s[j]相等之外,dp[j + 1][i - 1]若为真,就是回文串,通过以上分析,可以写出递推式如下: dp[i, j] = 1 if i == j = s[i] == s[j] if j = i + 1 = s[i] == s[j] && dp[i + 1][j - 1] if j > i + 1 这里有个有趣的现象,就是如果我把下面的代码中的二维数组由int改为vector<vector<int> >后,就会超时,这说明int型的二维数组访问执行速度完爆std的vector,所以,以后尽还是可能用最原始的数据类型吧。 C++参考答案二: 最后要提的就是大名鼎鼎的马拉车算法Manacher's Algorithm,这个算法的神奇之处在于将时间复杂度提升到了O(n)这种逆天的地步,而算法本身也设计的很巧妙,很值得我们掌握,参见博客:http://www.cnblogs.com/grandyang/p/4475985.html,代码实现如下: C++参考答案三: 官方解答: 摘要: 这篇文章是为中级读者而写的。它介绍了回文,动态规划以及字符串处理。请确保你理解什么是回文。回文是一个正读和反读都相同的字符串。 解决方案: 方法一:最长公共子串 常见错误 有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正): 算法: 我们可以看到,当 SS 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。 这给我们提供了一个复杂度为 O(n^2)O(n2) 动态规划解法,它将占用 O(n^2)O(n2) 的空间(可以改进为使用 O(n)O(n) 的空间)。请访问https://en.wikipedia.org/wiki/Longest_common_substring_problem阅读更多关于最长公共子串的内容。 方法二:暴力法 方法三:动态规划 方法四:中心扩展算法 Java代码: 方法五:Manacher算法 还有一个复杂度为 O(n) 的Manacher算法,你可以在该网站:https://articles.leetcode.com/longest-palindromic-substring-part-ii/找到详尽的解释。然而,这是一个非同寻常的算法,在45分钟的编码时间内提出这个算法将会是一个不折不扣的挑战。但是,请继续阅读并理解它,我保证这将是非常有趣的。 1 // Time complexity O(n*n)
2 class Solution {
3 public:
4 string longestPalindrome(string s) {
5 int startIdx = 0, left = 0, right = 0, len = 0;
6 for (int i = 0; i < s.size() - 1; ++i) {
7 if (s[i] == s[i + 1]) {
8 left = i;
9 right = i + 1;
10 searchPalindrome(s, left, right, startIdx, len);
11 }
12 left = right = i;
13 searchPalindrome(s, left, right, startIdx, len);
14 }
15 if (len == 0) len = s.size();
16 return s.substr(startIdx, len);
17 }
18 void searchPalindrome(string s, int left, int right, int &startIdx, int &len) {
19 int step = 1;
20 while ((left - step) >= 0 && (right + step) < s.size()) {
21 if (s[left - step] != s[right + step]) break;
22 ++step;
23 }
24 int wide = right - left + 2 * step - 1;
25 if (len < wide) {
26 len = wide;
27 startIdx = left - step + 1;
28 }
29 }
30 };
1 // DP
2 class Solution {
3 public:
4 string longestPalindrome(string s) {
5 int dp[s.size()][s.size()] = {0}, left = 0, right = 0, len = 0;
6 for (int i = 0; i < s.size(); ++i) {
7 for (int j = 0; j < i; ++j) {
8 dp[j][i] = (s[i] == s[j] && (i - j < 2 || dp[j + 1][i - 1]));
9 if (dp[j][i] && len < i - j + 1) {
10 len = i - j + 1;
11 left = j;
12 right = i;
13 }
14 }
15 dp[i][i] = 1;
16 }
17 return s.substr(left, right - left + 1);
18 }
19 };
1 class Solution {
2 public:
3 string longestPalindrome(string s) {
4 string t ="$#";
5 for (int i = 0; i < s.size(); ++i) {
6 t += s[i];
7 t += '#';
8 }
9 int p[t.size()] = {0}, id = 0, mx = 0, resId = 0, resMx = 0;
10 for (int i = 0; i < t.size(); ++i) {
11 p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
12 while (t[i + p[i]] == t[i - p[i]]) ++p[i];
13 if (mx < i + p[i]) {
14 mx = i + p[i];
15 id = i;
16 }
17 if (resMx < p[i]) {
18 resMx = p[i];
19 resId = i;
20 }
21 }
22 return s.substr((resId - resMx) / 2, resMx - 1);
23 }
24 };
反转 S,使之变成 S'。找到 S 和 S′之间最长的公共子串,这也必然是最长的回文子串。
1 public String longestPalindrome(String s) {
2 int start = 0, end = 0;
3 for (int i = 0; i < s.length(); i++) {
4 int len1 = expandAroundCenter(s, i, i);
5 int len2 = expandAroundCenter(s, i, i + 1);
6 int len = Math.max(len1, len2);
7 if (len > end - start) {
8 start = i - (len - 1) / 2;
9 end = i + len / 2;
10 }
11 }
12 return s.substring(start, end + 1);
13 }
14
15 private int expandAroundCenter(String s, int left, int right) {
16 int L = left, R = right;
17 while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
18 L--;
19 R++;
20 }
21 return R - L - 1;
22 }