题目描述
解法一:暴解,超时了(C++实现)
class Solution {
public:
bool isPalindromic(string s){
int len = s.length();
for(int i=0;i<len/2;i++)
{
if(s.at(i)!=s.at(len-i-1)) return false;
}
return true;
}
public:
string longestPalindrome(string s) {
string ans="";
int maxlen = 0;
int len = s.length();
for(int i=0;i<len;i++)
{
for(int j=i+1;j<=len;j++)
{
string test = s.substr(i, j-i+1);
if(isPalindromic(test)&&test.length()>maxlen)
{
ans = s.substr(i, j-i+1);
int ans_len = ans.length();
maxlen = max(maxlen, ans_len);
}
}
}
return ans;
}
};
讲几个知识点:
- C++提取子串可以参考:C++中在字符串中提取子字符串
- C++中bool和boolean的区别:
bool是c++中的一个关键字,属于c++类型的一种
boolean是定义来的
typedef unsigned char boolean; #define true 1 #define false 0 - string.at(n) 返回下标为n的元素的引用
解法二:中心扩展法(C++实现)
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n - 1 个这样的中心。
你可能会问,为什么会是 2n - 1 个,而不是 n 个中心?
因为回文的中心要区分单双。
假如回文的中心为 双数,例如 abba,那么可以划分为 ab bb ba,对于n长度的字符串,这样的划分有 n-1 种。
假为回文的中心为 单数,例如 abcd, 那么可以划分为 a b c d, 对于n长度的字符串,这样的划分有 n 种
对于 n 长度的字符串,我们其实不知道它的回文串中心倒底是单数还是双数,所以我们要对这两种情况都做遍历,也就是 n+(n-1) = 2n - 1,所以时间复杂度为 O ( n ) O(n) O(n)
当中心确定后,我们要围绕这个中心来扩展回文,那么最长的回文可能是整个字符串,所以时间复杂度为 O ( n ) O(n) O(n)
所以总时间复杂度为 O ( n 2 ) O(n^2) O(n2)
class Solution {
public:
string longestPalindrome(string s) {
if(!s.length()) return s;
int start = 0, end = 0;
for(int i=0;i<s.length();i++)
{
int len1 = expandAroundCenter(s,i, i); // 一个元素为中心
int len2 = expandAroundCenter(s, i, i+1); // 两个元素为中心
int len = max(len1, len2);
if(len>end-start+1)
{
start = i-(len-1)/2;
end = i+len/2;
}
}
return s.substr(start, end-start+1);
}
int expandAroundCenter(string s, int left, int right){
int l = left, r = right;
while(l>=0 && r<s.length() && s[l]==s[r])
{
l--;
r++;
}
return r-l-1;
}
};
class Solution {
public:
string longestPalindrome(string s) {
string res = "";
for (int i=0; i < s.size(); i++)
{
string s1 = palindrome(s, i, i);
string s2 = palindrome(s, i, i + 1);
res = res.size() > s1.size() ? res : s1;
res = res.size() > s2.size() ? res : s2;
}
return res;
}
string palindrome(string s, int l, int r){
while (l >= 0 && r < s.size() && s[l] == s[r])
{
l--;
r++;
}
return s.substr(l + 1, r - l - 1);
}
};
解法三:Manacher算法(C++实现)
可以参考
Manacher马拉车算法
class Solution {
public:
string longestPalindrome(string s) {
//插入"#"
string s1 ="$#";
for(auto c:s)
{
s1 += c;
s1 += '#';
}
vector<int> p(s1.length(),0);
//maxight表示某个回文串延伸在最右端半径的下标,center表示这个回文子串最中间位置下标
//resLen表示对应在s中的最大子回文串的半径,resCenter表示最大子回文串的中间位置
int maxright = 0, center = 0, reslen = 0, rescenter = 0;
//建立p数组
for(int i=1;i<s1.length();i++)
{
//更新p[i]的值
p[i] = maxright>i?min(p[2*center-i],maxright-i):1;
//遇到三种特殊的情况,都需要利用中心扩展法
while(s1[i+p[i]]==s1[i-p[i]]) p[i]++;
//半径下标i+p[i]超过边界mx,需要更新
if(maxright<i+p[i]){
maxright = i+p[i];
center = i;
}
//更新最大回文子串的信息
if(reslen<p[i]){
reslen = p[i];
rescenter = i;
}
}
//最长回文子串长度为半径-1,起始位置为中间位置减去半径再除以2
return s.substr((rescenter-reslen)/2, reslen-1);
}
};
复杂度分析:
时间复杂度:
O
(
N
)
O(N)
O(N),由于 Manacher 算法只有在遇到还未匹配的位置时才进行匹配,已经匹配过的位置不再匹配,因此对于字符串 s 的每一个位置,都只进行一次匹配,算法的复杂度为
O
(
N
)
O(N)
O(N)
空间复杂度:
O
(
N
)
O(N)
O(N)
讲几个知识点:
- 关于 auto 的内容参见 C++auto关键字的使用
- 关于 vector 定义的内容参见 C++基础之向量Vector
解法四:线性规划(C++)
我们给出 P ( i , j ) P(i,j) P(i,j) 的定义如下:
P ( i , j ) = { true, 如果子串 S i … S j 是回文子串 false, 其它情况 P(i,j) = \begin{cases} \text{true,} &\quad\text{如果子串} S_i \dots S_j \text{是回文子串}\\ \text{false,} &\quad\text{其它情况} \end{cases} P(i,j)={true,false,如果子串Si…Sj是回文子串其它情况
因此, P ( i , j ) = ( P ( i + 1 , j − 1 ) and S i = = S j ) P(i, j) = ( P(i+1, j-1) \text{ and } S_i == S_j ) P(i,j)=(P(i+1,j−1) and Si==Sj)
基本示例如下:
P
(
i
,
i
)
=
t
r
u
e
P(i, i) = true
P(i,i)=true
P
(
i
,
i
+
1
)
=
(
S
i
=
=
S
i
+
1
)
P(i, i+1) = ( S_i == S_{i+1} )
P(i,i+1)=(Si==Si+1)
这产生了一个直观的动态规划解法,我们首先初始化一字母和二字母的回文,然后找到所有三字母回文,并依此类推…
复杂度分析
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂:
O
(
n
2
)
O(n^2)
O(n2)
class Solution {
public:
string longestPalindrome(string s) {
if(s.length()<=1) return s;
int strlen = s.length();
int maxlen = 1;
string ans;
ans= s[0];
vector<vector<bool>> dp(strlen, vector<bool>(strlen));
for(int j=0; j<strlen;j++)
{
for(int i=j;i>=0;i--)
{
if((s[i]==s[j]) && ((j-i<=2) || dp[i+1][j-1]))
{
dp[i][j] = true;
if(j-i+1>maxlen)
{
maxlen = j-i+1;
ans = s.substr(i, maxlen);
}
}
}
}
return ans;
}
};