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"
题目大意:找最长的回文串,挺经典的一题,官方题解给了5种,这里有其中4种的C++版,有机会再更新。
Tips:
substr的用法:
①s.substr(start,length):从s的第i位开始,复制length长度;
②s.substr(start):从s的第i位开始,一直到s结束。
一、暴力法
时间O(n^3),空间O(1)
┭┮﹏┭┮通过时间127ms,虽然也能通过,还是尽量少用暴力吧,复杂度伤不起。
另外最外层的循环是用子字符串的串的长度作为变量,从最长的开始遍历,不要从0开始,这样找到得第一个满足条件的子字符串就可以了,能省点时间是一点emmmmm。
class Solution {
public:
string longestPalindrome(string s) {
string result;
for(int len=s.length();len>0;len--){
for(int i=0;(i+len)<=s.length();i++){
bool flag=false;
for(int j=0;j<=len/2;j++){
if(s[j+i]!=s[i+len-j-1])
break;
if(j==(len-1)/2)
flag=true;
}
if(flag==true)
return s.substr(i,len);
}
}
return result;
}
};
二、中心扩展法(强推,容易理解)
时间O(n^2),空间O(n^2)
通过时间20ms!
中心扩展法,其实我个人总有一种他是暴力和Manacher算法的结合,它的本质也是挨个遍历,找最大的,但比起暴力的优点是他以每个字符为遍历对象,由该字符串向外扩展,找到以该字符串为中心的最长的回文串。
C++
class Solution {
public:
string longestPalindrome(string s) {
string ans = "", temp;
for(int i = 0; i < s.length(); ++ i){
temp = expandToCenter(s, i, i);
ans = ans.length() > temp.length() ? ans : temp;
temp = expandToCenter(s, i, i + 1);
ans = ans.length() > temp.length() ? ans : temp;
}
return ans;
}
string expandToCenter(string s, int left, int right){
while(left >= 0 && right < s.length() && s[left] == s[right]){
-- left;
++ right;
}
return s.substr(left+1, right - left -1);
}
};
Python
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
self.ans = ""
self.s = s
for i in range(len(s)):
self.expandToCenter(i,i)
self.expandToCenter(i,i+1)
return self.ans
def expandToCenter(self, left, right):
while left >= 0 and right < len(self.s) and self.s[left] == self.s[right]:
left -= 1
right += 1
temp = self.s[left+1:right]
if len(self.ans) < len(temp):
self.ans = temp
三、Manacher算法
时间O(n),空间O(n)
具体原理,有些地方我也没想太透,有兴趣可以找找相关资料。
具体代码如下:
C++
class Solution {
public:
string longestPalindrome(string s) {
if(s.length()==0)
return s;
string s1=INIT(s);
string s2=Manancher(s1);
s1="";
for(int i=0;i<s2.length();i++){
if(s2[i]!='#')
s1+=s2[i];
}
return s1;
}
string INIT(string s){
int len=s.length();
string temp;
temp+='@';
for(int i=0;i<len;i++){
temp+='#';
temp+=s[i];
}
temp+="#$0";
return temp;
}
string Manancher(string s){
int len=s.length();
std::vector<int> Len(len,0);
int mx=0,po=0,ans=0;
string result;
for(int i=0;i<len;i++){
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);
else
Len[i]=1;
while(s[i-Len[i]]==s[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx){
mx=Len[i]+i;
po=i;
}
if(Len[i]>ans){
ans=Len[i];
result=s.substr(i-Len[i]+1,2*Len[i]-1);
}
}
return result;
}
};
Python
class Solution(object):
def longestPalindrome(self, s):
if len(s) == 0:
return s
s1 = self.INIT(s)
s2 = self.Manacher(s1)
ans = ""
for i in range(len(s2)):
if s2[i] != "#":
ans += s2[i]
return ans
def INIT(self, s):
temp = "@"
for i in range(len(s)):
temp += '#'
temp += s[i]
temp += "#$0"
return temp
def Manacher(self, s):
Len = [0] * (len(s))
mx, po, ans = 0,0,0
res = ""
for i in range(len(s)):
if mx > i:
Len[i] = min(mx-i, Len[2*po-i])
else:
Len[i] = 1
while (i+Len[i]) < len(s) and s[i-Len[i]] == s[i+Len[i]]:
Len[i] += 1
if Len[i] + i > mx:
mx = Len[i] + i
po = i
if Len[i] > ans:
ans = Len[i]
res = s[i-Len[i]+1:i+Len[i]]
return res
以下是manacher的核心算法
string s;//原字符串
string temp;//原字符串插入特殊字符
string result,tempresult;//最终结果子字符串
//形成temp字符串
void INIT(string s){
int len=s.length();
temp+='@';
for(int i=0;i<len;i++){
temp+='#';
temp+=s[i];
}
temp+="#$0";
}
//核心算法
void Manacher(string s){
int len=s.length();
std::vector<int> Len(len,0);
int mx=0,ans=0,po=0;
for(int i=0;i<len;i++){
Len[i]=mx>i? min(mx-i,Len[2*po-i]) : 1;
while(s[i-Len[i]]==s[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx){
mx=Len[i]+i;
po=i;
}
if(Len[i]>ans){
ans=Len[i];
tempresult=s.substr(i-Len[i]+1,2*Len-1);
}
}
}
int main(){
INIT(s);
Manacher(s);
for(int i=0;i<tempresult.length();i++)
if(tempresult[i]!='#')
result+=tempresult[i];
}
四、动态规划
时间O(n^2),空间O(n^2)
dp[i][j]表示i-j的子字符串是否是回文串,初始化dp数组均为false,首先遍历s,将dp数组的对角线改为true,以及能构成两个字符长的子字符串的i处dp修改,再进行遍历时,k表示子字符串的长度,若从i开始的k个字符能构成回文串,则需要i+1处开始的k-2个字符能构成回文串,同时要i和i+k-1处的字符相等。
C++
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length(), cnt = 1;
if(len == 0)
return s;
vector<vector<bool>> dp(len, vector<bool>(len, false));
string ans = s.substr(0, 1);
for(int i = 0; i < len; ++ i){
dp[i][i] = true;
if(i < len - 1 && s[i] == s[i+1]){
if(cnt == 1)
ans = s.substr(i, ++cnt);
dp[i][i+1] = true;
}
}
for(int k = 3; k <= len; ++ k){
for(int i = 0; i + k <= len; ++ i)
if(s[i] == s[i+k-1] && dp[i+1][i+k-2]){
if(cnt < k){
cnt = k;
ans = s.substr(i, cnt);
}
dp[i][i+k-1] = true;
}
}
return ans;
}
};
Python
class Solution(object):
def longestPalindrome(self, s):
Len = len(s)
if Len == 0:
return s
dp = [[False] * Len for i in range(Len)]
ans = "" + s[0]
for i in range(Len-1):
dp[i][i] = True
if(s[i] == s[i+1]):
dp[i][i+1] = True
if(len(ans) == 1):
ans = s[i:i+2]
dp[Len-1][Len-1] = True
for k in range(3,Len+1):
for i in range(Len+1-k):
if dp[i+1][i+k-2] and s[i] == s[i+k-1]:
dp[i][i+k-1] = True
if k > len(ans):
ans = s[i:i+k]
return ans