问题:给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
方法一:暴力解法
容易想到的方法往往耗时。思路是酱紫哒:一段字符串可能的最短回文是单个字符,可能的最长回文是它本身。那我们就设定一个窗口,长度从1到 len(s)。若有窗口长度w,依次以每个字符为起点,取长度为w的字符串,注意最后一个起点不可能是最后一个字符,否则窗口就填不满了。判断所取的字符串和它的反转字符串是否相同,若相同这个字符串就是回文。记录各次查找的最长回文。时间复杂度为O(n³),代码中虽然只有两重for循环,但第三次其实是b = a[::-1],这个反转过程是循环产生的。
#Python3
class Solution:
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
l = 0;ans = "";a = b =""
for w in range(1,len(s)+1): #w为窗口长度
for i in range(0,len(s)-w+1): #每个窗口的起始位置,最后一个起点应是最后一个窗口的开头
a = s[i:w+i];b = a[::-1] #取这段窗口的字符串即其逆置字符串
if a == b: #如果这个窗口内的字符串和它的逆置字符串相等,则它是回文
if len(a) > l: #如果这段回文比之前的回文长,那把这段回文作为最新答案
l = len(a)
ans = a
return ans
方法二:动态规划
这个方法是对暴力解法的一个改进。暴力解法每次都要取字符串并进行判断,相当于把所有情况逐一列举出来。但动态规划对每次进行判断时,只要知道上一次是什么情况就可以了,也就是说只要我知道一个初始状态,我就可以从这个初始状态出发,根据一定的规则,依次推导出后面各个状态。首先定义 P(start,end),对于字符串s[start,end],如果它是回文,那么P(start,end)为True,否则为False。那怎么判断s[start,end]是否是回文?如果把该字符串两端去掉后,剩下的部分即s[start+1,end-1]是回文,那么只要s[start]==s[end],即新加上的两端相同,那么s[start,end]一定是回文。但是长度为1和2时,不能用上面的方法,长度为2时,假设我们求P(1,2),那么s[start+1,end-1]为s[2,1],实际情况不可能出现。对于p[start][end] = (w == 1 or w == 2 or p[start+1][end-1]) and (s[start] == s[end]),长度为1时,肯定是回文,长度为2时,因为要同时满足s[start] == s[end],所以长度为2也是回文。算法时间复杂度是O(n²)。
#Python
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
maxL = 0;l = len(s);ans = "" #maxL记录最大长度
p = [[False for _ in range(l)]for _ in range(l)] #生成二维数组
for w in range(1,l+1): #从1开始取窗口大小
for start in range(0,l): #设置起点
end = start + w - 1
if end >= l: #如果终点超出所给字符总长,不合要求,结束本次循环
break
p[start][end] = (w == 1 or w == 2 or p[start+1][end-1]) and (s[start] == s[end]) #判断条件
if p[start][end] and w > maxL: #如果是回文且比之前的回文都长
maxL = w;ans = s[start:end+1]
return ans
//C++
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if(len == 0) return "";
bool p[len][len];memset(p, false, sizeof(p)); //memset()对p进行初始化
int maxL = 0;
string ans = "";
for(int w = 1;w <= len;w++){
for(int start = 0;start < len;start++){
int end = start + w - 1;
if(end >= len)
break;
p[start][end] = (w == 1 || w == 2 || p[start+1][end-1]) && (s[start] == s[end]);
if(p[start][end] && w > maxL){
maxL = w;
ans = s.substr(start,w);
}
}
}
return ans;
}
};
其实官方总共给了5中解法,但是我觉得还是要根据自己的情况从自己能掌握的开始。后面孰能生巧了,再去掌握那些更高级的算法。