题目描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
输入: "cbbd"
输出: "bb"
题目链接: https://leetcode-cn.com/problems/longest-palindromic-substring/
思路1
暴力法。求出所有的子串,然后判断子串是否是回文串,保留最长的回文串。代码如下:
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return "";
if(s.length()==1) return s;
string ans = "";
for(int i=0; i<s.length(); i++){
for(int j=0; j<=s.length()-i; j++){
string ss = s.substr(i,j);
if(ss.length()>ans.length() && isOk(ss)){
ans = ss;
}
}
}
return ans;
}
bool isOk(string s){
int left=0;
int right=s.length()-1;
while(left<right){
if(s[left]!=s[right]){
return false;
}
left++;
right--;
}
return true;
}
};
- 时间复杂度:O(n^3)
外面两层循环,中间的substr应该也要O(n)(没搜到substr的时间复杂度),所以为O(n^3)。 - 空间复杂度:O(1)
该方法超时未通过。
思路2
如果一个字符串是回文串,那么从该字符串中心开始左右是对称的。所以,我们可以遍历字符串,当遍历到某个字符时,设置两个指针,以该字符为中心向左右扩展,并统计左右两边相等的字符个数,也就是回文串的长度。需要注意的是,当字符串长度为奇数时,如aba,则两个指针都从b开始扩展即可;如果字符串长度为偶数,如abba,这样如果两个指针都从同一位置开始则找不到回文串,尽管该字符串就是一个回文串。为了避免这个问题,对于每个字符我们要左右扩展两次:第一次左右指针从同一位置出发,第二次右指针从左指针的下一个位置出发(i和i+1)。代码如下:
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return "";
if(s.length()==1) return s;
int maxLen = -1;
string ans = "";
int start=0, end=0;
for(int i=0; i<s.length(); i++){
int len1 = spanAroundCenter(s, i, i);
int len2 = spanAroundCenter(s, i, i+1);
int len = max(len1, len2);
if(len>maxLen){
maxLen = len;
if(len2>len1){
start = i-len/2+1; // 注意这个
end = i+len/2;
}else{
start = i-len/2;
end = i+len/2;
}
}
}
return s.substr(start, end-start+1);
}
int spanAroundCenter(string s, int left, int right){
int len = 0;
while(left>=0 && right<s.length()){
if(s[left]==s[right]){
len++;
} else return right-left-1; // 不能直接返回len,直接返回len无法区别是哪种扩展方式
left--;
right++;
}
return right-left-1;
}
};
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
更简洁的写法
下面的写法更加简洁,也更快:
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return "";
if(s.length()==1) return s;
int n = s.length();
string ans = "";
string temp = "";
int curMaxLen = -1;
for(int center=0; center<2*n-1; center++){
int left = center/2;
int right = left+center%2;
while(left>=0 && right<n && s[left]==s[right]){
if(right-left+1>curMaxLen){ // right-left+1是当前的字符串长度
ans = s.substr(left, right-left+1);
curMaxLen = right-left+1;
}
left--;
right++;
}
}
return ans;
}
};
这样写更容易理解:
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
int n = 2*s.size()-1;
string ans = "";
for(int i=0; i<n; i++){
int left = i/2;
int right = (i+1)/2;
while(left>=0 && right<s.size() && s[left]==s[right]){
left--;
right++;
}
left++;
right--;
if(ans.size()<right-left+1){
ans = s.substr(left, right-left+1);
}
}
return ans;
}
};