5. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.
Example 2:
Input: "cbbd"
Output: "bb"
给一个字符串s,找出s中所包含的最长回文字符串(回文字符串:即关于某一个字符或某个位置呈对称,比如aba、abba、adsda就是,abc、abdc就不是),s的长度最长为1000个字符。
题意很简单,给出以下几种解法
class Solution {
public:
string longestPalindrome(string s) {
int max=1;
string ans=s.substr(0,1);
for(int i=0; i1; --j){
int imin=i, imax= i+j-1;
while(imin<=imax && s[imin]==s[imax]){
++imin;--imax;
}
if(imin>imax){
if(j>max){
max=j;
ans = s.substr(i,j);
}
}
}
}
return ans;
}
};
做这道题想到的第一种思路,一开始本地实现之后,上传之后直接爆掉了,后加修正完善之后,勉强通过,但依旧没有效率可言。结果如下:
Runtime: 2744 ms, faster than 2.99% of C++ online submissions for Longest Palindromic Substring.
Memory Usage: 9.4 MB, less than 100.00% of C++ online submissions for Longest Palindromic Substring.
没错,这是C++实现的结果,效率恐怕相比python都要慢一个量级。分析原因可以发现,每次循环判断都是从头开始,因此包含了大量的无用计算,比如已经判断出ada为回文,那么这时直接判断ada两边的字符即可判断出当前位置是否存在以字母d为中心长度更长的回文字符串,比如xadax。所以如果使用展开的方式替换上面暴力循环的方式 能够节省很多不必要的计算。实现如下:
class Solution {
public:
// 求从 left 和 right开始展开 符合回文的最长字符串长度
int expandCenter(string &s, int left, int right){
int l=left, r=right;
while(l>=0 && r(e-st+1)){
// (len-1)/2 表示取得的回文串在展开中心最前面的字符
st = i - (len-1)/2;
e = i + len/2;
}
}
return s.substr(st,e-st+1);
}
};
平均时间复杂度为O(n2),结果如下:
Runtime: 24 ms, faster than 84.23% of C++ online submissions for Longest Palindromic Substring.
Memory Usage: 9.4 MB, less than 100.00% of C++ online submissions for Longest Palindromic Substring.
可以看出,使用展开的方式 虽然还不是最优解,但相比之前暴力解法 效率也有了极大的提升。
对比方法2和方法1,效率提升的原因时因为我们去除了大量不必要的循环冗余,那么遵循同样的思路 可不可以用来再次改善方法2呢?
观察方法2可以得知,是通过在每一个字符串开始呈镜像搜索匹配,那么这里是否有必要呢? 比如字符串 "baaaaaaaaaaaaab" 在除最中间的a以外的展开都是徒劳无用的,因此 方法3主要针对这种情况进行完善,方法也很简单 遇到相邻且相同的字符,可以直接以这些相同的字符构成的字符串为中心进行展开搜索,实现如下:
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<=0) return "";
if(s.size()==1) return s;
int imin=0, imax=0, maxlen=0;
int i=0, len=s.size();
while(i0 && s[sr+1]==s[sl-1]){
++sr;
--sl;
}
int nowLen = sr-sl+1;
if(nowLen>maxlen){
maxlen = nowLen;
imin = sl;
}
}
return s.substr(imin,maxlen);
}
};
Runtime: 8 ms, faster than 100.00% of C++ online submissions for Longest Palindromic Substring.
Memory Usage: 9.4 MB, less than 100.00% of C++ online submissions for Longest Palindromic Substring.
对比可以发现,算法执行效率相比前面两种已经有了质的提升。
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
if(len<=0) return "";
if(len==1) return s;
int a[len][len];
for(int i=0;i
本方法使用动态规划解决,通过观察可以发现,最长回文串一定是基于前面回文串向两端扩展得到的。因此,每一个位置判断是否为回文,只需要在前面得到的结果上面加一些条件即可,比如P(i,j) = P(i-1,j+1) and str[i]==str[j] 。
上面解决方法中,我们使用二维数组a表示是否字符串[i,j] 是否为回文串,初始只有单个字符为回文,后面每一步判断均参照前面的结果。
Runtime: 156 ms, faster than 36.93% of C++ online submissions for Longest Palindromic Substring.
Memory Usage: 13.9 MB, less than 100.00% of C++ online submissions for Longest Palindromic Substring.
可以看出,针对本题使用DP并不是一个很好的解决方案,但在其他场景下DP往往是至关重要的解决方案。
class Solution:
def longestPalindrome(self, s: 'str') -> 'str':
slen = len(s)
if slen<=0: return ""
if slen==1: return s
imin, imax=0, 0
maxlen=1
for i in range(slen):
sl, sr = i, i
while sl0 and slmaxlen:
maxlen = nowlen
imin = sr
imax = sl
return s[imin:imax+1]
Runtime: 940 ms, faster than 72.81% of Python3 online submissions for Longest Palindromic Substring.
Memory Usage: 12.4 MB, less than 100.00% of Python3 online submissions for Longest Palindromic Substring.
可见,对于python而言 C++的最优解题思路并不一定是最适合python 的方法。具体如何提升python结果的效率,大家可以多做尝试。
Status:ACCEPT