Medium!

题目描述:

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 长度最长为1000。

示例:

输入: "babad"

输出: "bab"

注意: "aba"也是有效答案

示例:

输入: "cbbd"

输出: "bb"

回文串概念:

“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

回文算法描述:

1、初始化标志flag=true;
2、输入字符串str,并获取其长度len;
3、定义并初始化游标i=0,j=len-1,分别指向字符串开头和末尾;
4、比较字符str[i]和str[j],若i==j,转至7,否则往下执行5;
5、若str[i]和str[j]相等,则游标i加1,游标j减1后转至4,否则往下执行6;
6、令标志位flag=flase,结束比较,str不是回文串,算法结束。
7、若str[i]和str[j]相等,结束比较,flag=true,str为回文串,算法结束
 #include <iostream>
#include <string>
using namespace std; int main()
{
string str;
int i,j,l;
int flag = ; while (cin >> str)
{
l = str.length();
for (i = ,j = l-; i <= j; i++,j--)
{
if (str[i] != str[j])
{
flag = ;
}
}
if (flag) cout << "YES" << endl;
else cout << "NO" << endl;
flag = true;
} return ;
}

解题思路:

LeetCode中关于回文串的题共有五道,除了这道,其他的四道为Palindrome Number 验证回文数字Validate Palindrome 验证回文字符串Palindrome Partitioning 拆分回文串Palindrome Partitioning II 拆分回文串之二,我们知道传统的验证回文串的方法就是两个两个的对称验证是否相等,那么对于找回文字串的问题,就要以每一个字符为中心,像两边扩散来寻找回文串,这个算法的时间复杂度是O(n*n),可以通过OJ,就是要注意奇偶情况,由于回文串的长度可奇可偶,比如"bob"是奇数形式的回文,"noon"就是偶数形式的回文,两种形式的回文都要搜索。这道题让我们求最长回文子串。

C++参考答案一:

 // Time complexity O(n*n)
class Solution {
public:
string longestPalindrome(string s) {
int startIdx = , left = , right = , len = ;
for (int i = ; i < s.size() - ; ++i) {
if (s[i] == s[i + ]) {
left = i;
right = i + ;
searchPalindrome(s, left, right, startIdx, len);
}
left = right = i;
searchPalindrome(s, left, right, startIdx, len);
}
if (len == ) len = s.size();
return s.substr(startIdx, len);
}
void searchPalindrome(string s, int left, int right, int &startIdx, int &len) {
int step = ;
while ((left - step) >= && (right + step) < s.size()) {
if (s[left - step] != s[right + step]) break;
++step;
}
int wide = right - left + * step - ;
if (len < wide) {
len = wide;
startIdx = left - step + ;
}
}
};

此题还可以用动态规划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++参考答案二:

 // DP
class Solution {
public:
string longestPalindrome(string s) {
int dp[s.size()][s.size()] = {}, left = , right = , len = ;
for (int i = ; i < s.size(); ++i) {
for (int j = ; j < i; ++j) {
dp[j][i] = (s[i] == s[j] && (i - j < || dp[j + ][i - ]));
if (dp[j][i] && len < i - j + ) {
len = i - j + ;
left = j;
right = i;
}
}
dp[i][i] = ;
}
return s.substr(left, right - left + );
}
};

最后要提的就是大名鼎鼎的马拉车算法Manacher's Algorithm,这个算法的神奇之处在于将时间复杂度提升到了O(n)这种逆天的地步,而算法本身也设计的很巧妙,很值得我们掌握,参见博客:http://www.cnblogs.com/grandyang/p/4475985.html,代码实现如下:

C++参考答案三:

 class Solution {
public:
string longestPalindrome(string s) {
string t ="$#";
for (int i = ; i < s.size(); ++i) {
t += s[i];
t += '#';
}
int p[t.size()] = {}, id = , mx = , resId = , resMx = ;
for (int i = ; i < t.size(); ++i) {
p[i] = mx > i ? min(p[ * id - i], mx - i) : ;
while (t[i + p[i]] == t[i - p[i]]) ++p[i];
if (mx < i + p[i]) {
mx = i + p[i];
id = i;
}
if (resMx < p[i]) {
resMx = p[i];
resId = i;
}
}
return s.substr((resId - resMx) / , resMx - );
}
};

官方解答:

摘要:

这篇文章是为中级读者而写的。它介绍了回文,动态规划以及字符串处理。请确保你理解什么是回文。回文是一个正读和反读都相同的字符串。

解决方案:

方法一:最长公共子串

常见错误

有些人会忍不住提出一个快速的解决方案,不幸的是,这个解决方案有缺陷(但是可以很容易地纠正):

反转 S,使之变成 S'。找到 S 和 S​′之间最长的公共子串,这也必然是最长的回文子串。

算法:

我们可以看到,当 SS 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。

这给我们提供了一个复杂度为 O(n^2)O(n​2​​) 动态规划解法,它将占用 O(n^2)O(n​2​​) 的空间(可以改进为使用 O(n)O(n) 的空间)。请访问https://en.wikipedia.org/wiki/Longest_common_substring_problem阅读更多关于最长公共子串的内容。

方法二:暴力法

方法三:动态规划

方法四:中心扩展算法

Java代码:

 public String longestPalindrome(String s) {
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
} private int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}

方法五:Manacher算法

还有一个复杂度为 O(n) 的Manacher算法,你可以在该网站:https://articles.leetcode.com/longest-palindromic-substring-part-ii/找到详尽的解释。然而,这是一个非同寻常的算法,在45分钟的编码时间内提出这个算法将会是一个不折不扣的挑战。但是,请继续阅读并理解它,我保证这将是非常有趣的。

最新文章

  1. 网页3D引擎“Babylon.JS”入门教程翻译总结
  2. 我的毕业设计——基于安卓和.NET的笔记本电脑远程控制系统
  3. 日志记录类库log4net的使用总结
  4. SVN错误:Attempted to lock an already-locked dir
  5. webview 实现滑动前进后退功能
  6. Vue2.0 vue-source.js jsonp demo vue跨域请求
  7. C# 字符串计算表达式
  8. 【转】Hibernate各种主键生成策略与配置详解
  9. C语言--流程控制
  10. Linux常用命令汇总及使用方法(一)
  11. 一起看看2016中国第三届CSS开发者大会有哪些大咖演讲
  12. Linux权限相关操作命令
  13. hdu 5739 割点
  14. 无法在Application Designer中打开PeopleTools对象
  15. Python大数据系列-01-关系数据库基本运算
  16. google colab 使用指南
  17. java.util.Stack类中 empty() 和 isEmpty() 方法的作用
  18. HDU - 6311 Cover(无向图的最少路径边覆盖 欧拉路径)
  19. 把Oracle由归档模式改为非归档模式
  20. MyCat原理及分布式分库分表

热门文章

  1. 增加swap分区,文件形式
  2. 启动tomcat时报错:java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException:A child container failed during start
  3. pandas简短介绍
  4. CodeForces701E DFS
  5. iframe伪造ajax
  6. 循环屏障CyclicBarrier以及和CountDownLatch的区别
  7. Python基础-day03
  8. vue自学入门-1(Windows下搭建vue环境)
  9. OGG实现两台Oracle数据库的同步
  10. java 多线程二