5. Longest Palindromic Substring

  • Total Accepted: 120226
  • Total Submissions: 509522
  • Difficulty: Medium

  Given a string s, find the longest palindromic(回文) substring in sS. You may assume that the maximum length of s is 1000, and there exists one unique longest palindromic substring.

  推荐解法3,直观,有效,好理解

方法一:暴力搜索(O(N³))

  这个思路就很简单了,就是直接求出每一个子串,然后判断其是否为回文。我们从子串长度最长开始,依次递减,如果遇到是回文的,则直接返回即可。循环结束如果没有回文,则返回null。

  值得注意的一点是因为题目直接说了肯定存在回文,并且最大长度的回文唯一,所以在当字符串长度大于1的时候,最大回文长度必定大于1(如果为1的话,则每一个单独字符都可以作为最长长度的回文),所以搜索长度递减到2就结束了。题目中肯定存在大于2的回文,所以不会直到最后循环结束返回null这一步,所以最后直接写的返回null无关紧要。

 /*
* 方法一:暴力搜索
*/
public String longestPalindrome(String s) {
if (s == null || s.length() == 0 || s.length() == 1) {
return s;
} String sub;
for (int subLen = s.length(); subLen > 1; subLen--) {
for (int startIndex = 0; startIndex <= (s.length() - subLen); startIndex++) {
// 列出所有子串,然后判断子串是否满足有重复
if (startIndex != (s.length() - subLen)) {
sub = s.substring(startIndex, startIndex + subLen);
} else {
sub = s.substring(startIndex);
}
System.out.println(sub);
if (isPalindrome(sub)) {
return sub;
}
}
} return null ;
} private boolean isPalindrome(String sub) {
for(int i = 0 ; i <= sub.length()/2 ; i++){
if(sub.charAt(i) != sub.charAt(sub.length()-i-1)){
return false ;
}
}
return true ;
}

方法二:动态规划O(N²)

更简洁的做法,使用动态规划,这样可以把时间复杂度降到O(N²),空间复杂度也为O(N²)。做法如下:

首先,写出动态转移方程。

Define P[ i, j ] ← true iff the substring Si … Sj is a palindrome, otherwise false.

P[ i, j ] ← ( P[ i+1, j-1 ] and Si = Sj ) ,显然,如果一个子串是回文串,并且如果从它的左右两侧分别向外扩展的一位也相等,那么这个子串就可以从左右两侧分别向外扩展一位。

其中的base case是

P[ i, i ] ← true
P[ i, i+1 ] ← ( Si = Si+1 )

然后,看一个例子。

假设有个字符串是adade,现在要找到其中的最长回文子串。使用上面的动态转移方程,有如下的过程:

  按照红箭头->黄箭头->蓝箭头->绿箭头->橙箭头的顺序依次填入矩阵,通过这个矩阵记录从i到j是否是一个回文串。

 /*
* 方法二:动态规划的方法
*/
public String longestPalindrome2(String s) {
if (s == null || s.length() == 0 || s.length() == 1) {
return s;
}
char [] arr = s.toCharArray() ;
int len = s.length() ;
int startIndex = 0 ;
int endIndex = 0 ;
boolean [][] dp = new boolean [len][len] ;
dp[0][0] = true ;
for(int i = 1 ; i < len ; i++){
//dp[i][i]置为true
dp[i][i] = true ;
//dp[i-1][i]判断true或false
if(arr[i-1] != arr[i]){
dp[i-1][i] = false ;
}else{
dp[i-1][i] = true ;
startIndex = i-1 ;
endIndex = i ;
}
}
//填充其他地方的值
for(int l = 2 ; l < len ; l++){
for(int i = 0 ; i < len-l ; i++){
int j = i+l ;
if(dp[i+1][j-1] && (arr[i] == arr[j])){
dp[i][j] = true ;
if((j-i) > (endIndex - startIndex)){
startIndex = i ;
endIndex = j ;
}
}
}
}
//返回最长回文字符串
if(endIndex == (len-1)){
return s.substring(startIndex) ;
}else{
return s.substring(startIndex, endIndex+1) ;
}
}

  

  下面的方法参考自 http://blog.csdn.net/feliciafay/article/details/16984031

方法三:从中间向两边展开O(N²)(比动态规划方法好理解)

  回文字符串显然有个特征是沿着中心那个字符轴对称。比如aha沿着中间的h轴对称,a沿着中间的a轴对称。那么aa呢?沿着中间的空字符''轴对称。所以对于长度为奇数的回文字符串,它沿着中心字符轴对称,对于长度为偶数的回文字符串,它沿着中心的空字符轴对称。对于长度为N的候选字符串,我们需要在每一个可能的中心点进行检测以判断是否构成回文字符串,这样的中心点一共有2N-1个(2N-1=N-1 + N)。检测的具体办法是,从中心开始向两端展开,观察两端的字符是否相同。代码如下:

 //从中间向两边展开
string expandAroundCenter(string s, int c1, int c2) {
int l = c1, r = c2;
int n = s.length();
while (l >= && r <= n- && s[l] == s[r]) {
l--;
r++;
}
return s.substr(l+, r-l-);
} string longestPalindromeSimple(string s) {
int n = s.length();
if (n == ) return "";
string longest = s.substr(, ); // a single char itself is a palindrome
for (int i = ; i < n-; i++) {
string p1 = expandAroundCenter(s, i, i); //长度为奇数的候选回文字符串
if (p1.length() > longest.length())
longest = p1; string p2 = expandAroundCenter(s, i, i+);//长度为偶数的候选回文字符串
if (p2.length() > longest.length())
longest = p2;
}
return longest;
}

四、 时间复杂度为O(N)的算法

  在这里看到了更更简洁的做法,可以把时间复杂度降到O(N).具体做法原文说得很清楚,有图有例,可以仔细读读。这里我只想写写,为什么这个算法的时间复杂度是O(N)而不是O(N²)。从代码中看,for循环中还有个while,在2层嵌套的循环中,似乎应该是O(N²)的时间复杂度。

 // Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
string preProcess(string s) {
int n = s.length();
if (n == ) return "^$";
string ret = "^";
for (int i = ; i < n; i++)
ret += "#" + s.substr(i, ); ret += "#$";
return ret;
} string longestPalindrome(string s) {
string T = preProcess(s);
int n = T.length();
int *P = new int[n];
int C = , R = ;
for (int i = ; i < n-; i++) {
int i_mirror = *C-i; // equals to i' = C - (i-C) P[i] = (R > i) ? min(R-i, P[i_mirror]) : ; // Attempt to expand palindrome centered at i
while (T[i + + P[i]] == T[i - - P[i]])
P[i]++; // If palindrome centered at i expand past R,
// adjust center based on expanded palindrome.
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
} // Find the maximum element in P.
int maxLen = ;
int centerIndex = ;
for (int i = ; i < n-; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
delete[] P; return s.substr((centerIndex - - maxLen)/, maxLen);
}

  时间复杂度为什么是O(N)而不是O(N²)呢?

  假设真的是O(N²),那么在每次外层的for循环进行的时候(一共n步),对于for的每一步,内层的while循环要进行O(N)次。而这是不可能。因为p[i]和R是有相互影响的。while要么就只走一步,就到了退出条件了。要么就走很多很步。如果while走了很多步,多到一定程度,会更新R的值,使得R的值增大。而一旦R变大了,下一次进行for循环的时候,while条件直接就退出了。

LeetCode--No.005 Longest Palindromic Substring的更多相关文章

  1. 【LeetCode】005. Longest Palindromic Substring

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  2. 【JAVA、C++】LeetCode 005 Longest Palindromic Substring

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  3. 【一天一道LeetCode】#5 Longest Palindromic Substring

    一天一道LeetCode系列 (一)题目 Given a string S, find the longest palindromic substring in S. You may assume t ...

  4. 【LeetCode OJ】Longest Palindromic Substring

    题目链接:https://leetcode.com/problems/longest-palindromic-substring/ 题目:Given a string S, find the long ...

  5. 005 Longest Palindromic Substring 最长回文子串

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  6. LeetCode(3)题解: Longest Palindromic Substring

    https://leetcode.com/problems/longest-palindromic-substring/ 题目: Given a string S, find the longest ...

  7. 【LeetCode】5. Longest Palindromic Substring 最长回文子串

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 公众号:负雪明烛 本文关键词:最长回文子串,题解,leetcode, 力扣,python ...

  8. 【LeetCode】5. Longest Palindromic Substring 最大回文子串

    题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

  9. No.005 Longest Palindromic Substring

    5. Longest Palindromic Substring Total Accepted: 120226 Total Submissions: 509522 Difficulty: Medium ...

  10. 【leetcode】5. Longest Palindromic Substring

    题目描述: Given a string S, find the longest palindromic substring in S. You may assume that the maximum ...

随机推荐

  1. Docker 简介,入门

    1.简介 Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行 ...

  2. 结构体指offsetof宏详细解析

    1.#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)     (include/linux/stddef.h) ...

  3. TZOJ 2099 Sightseeing tour(网络流判混合图欧拉回路)

    描述 The city executive board in Lund wants to construct a sightseeing tour by bus in Lund, so that to ...

  4. FortiGate日常检查

    1.1)CPU利用率:由于防火墙有芯片,正常的流量都走芯片转发,不走cpu,只有开了utm相关的应用层防护功能和DDOS之类的,才会走cpu,所以一般cpu都不会占用太多,甚至很多时间都是0%, 如果 ...

  5. vue 高阶 provide/inject

    1.一般情况使用都是在app.vue配置为: provide () {return {isTest: this}}, 2.所有子组件都可以引用 拿到app.vue里面的所有数据 inject: ['i ...

  6. Linux驱动之输入子系统简析

    输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...

  7. Python开发——函数【装饰器、高阶函数、函数嵌套、闭包】

    装饰器 装饰器本质就是函数,为其他函数添加附加功能. 原则: 不修改被修饰函数的源代码 不修改被修饰函数的调用方法 装饰器知识储备:装饰器 = 高阶函数 + 函数嵌套 + 闭包 案例:求函数运行时间! ...

  8. Nodejs初识随笔

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效. Node.js 单线程运行,不 ...

  9. Python下安装MySQLdb模块

    ----------------------[针对Windows下python 的MySQLdb模块安装]--------------------- 一.检查MySQLdb模块是否安装,可在DOS命令 ...

  10. 全渠道java b2b b2c o2o平台

    大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B.B2C.C2C.O2O.新零售.直播电商等子平台. 根据微服务化设计思想,结合spring cloud一 ...