使用方法:Manacher方法(参考博客中叫做——马拉车算法,很幽默的博主。。)
参考博客:https://blog.youkuaiyun.com/HappyRocking/article/details/82527217
https://blog.youkuaiyun.com/HappyRocking/article/details/82622881
在这里鸣谢此博主,讲的通俗易懂,看过思想之后,代码写得很顺畅~
题目:
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"
分析:此题所求为最长回文子串,也就是字符串是的 子串 中 长度最长的 回文字符串。
方法一:暴力求解(时间复杂度:O(n^3))
思路:满足上面的两个条件,(1)求出字符串的子串s' (2)判断 s' 是否为回文字符串
C++代码:(当然仅作理解)
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if(n == 0) return "" ;
string res = s.substr(0,1) ;
/*
求子串:
i 代表要求的子串长度 ,这里从n开始递减,So只要找到第一个就是最长的回文子串
j 代表子串的起始位置,注意边界问题
由于两层for循环,所以当res值不为空时,就证明已有最长回文子串,可结束循环
*/
for(int i = n ; i > 1 ; i --){
for(int j = 0 ; j <= n - i ; j ++ ){
if(isPalindromic(s,j,i)){
res = s.substr(j,i);
break ;
}
}
if(res.size() != 1) break;
}
return res ;
}
// 判断字符串是否为回文串
int isPalindromic(string s, int i, int k){
int j = i + k - 1;
while(i < j){
if(s[i] == s[j]){
i ++;
j --;
}
else
return 0;
}
return 1;
}
};
方法二:中心扩展法(时间复杂度:O(n^2))
思路:从所谓的中心字符开始向外扩充,直到两字符不同
例:s = "asaffa"
1) 单字符串为中心:当‘s’为中心字符时 ,初始时回文串是‘s’本身,之后向两面扩大会问字符串长度——即判断s两边的字符是否相等,若相等则扩大半径。。。(可自行想象)
2)双字符串为中心:当‘ff’为中心字符串时,初始回文串是‘ff’,同样的向两侧扩展
想想这种方法要考虑到每次的中心字符为 一个 和 两个 的情况,但是第一层for循环s[i]的意义就是中心字符,两个中心字符的情况就考虑s[i]s[i + 1] ,所以就要第一层for循环最大长度为s.size() - 1,有人可能会想若中心字符为1个时,那么最后一个字符没考虑是不是对答案有影响?答案是NO,因为若最后一个字符为中心字符,最后的结果也就是它本身,而在他之前一定已经有了一个答案~ 具体过程详见代码
C++代码:
class Solution {
private: //最后结果,设为全局变量,方便更改,也可做为局部变量,修改过程需传参
string res = "" ;
public:
string longestPalindrome(string s) {
//由于第一层遍历时到最后一个字符的前一位
//所以空值和一个字符时并没处理
//方便的是这两种情况可直接返回答案
if(s.size() <= 1) return s ;
for(int i = 0 ; i < s.size() - 1 ; i ++){
freshPalindromic(s,i,i) ;
freshPalindromic(s,i,i + 1) ;
}
return res ;
}
更新全局变量——最后的回文串结果
void freshPalindromic(string s, int i, int j){
while(i >= 0 && j < s.size())
if(s[i] == s[j]){
i -- ;
j ++ ;
}
else
break ;
//无论是遍历到边界还是由于两字符不等退出while循环,都需要更改ij的值
i ++ ;
j -- ;
if(j - i + 1 > res.size())
res = s.substr(i,j - i + 1) ;
}
};
方法三:Manacher法(时间复杂度:O(n))
思路:还是借鉴大神的思路:https://blog.youkuaiyun.com/HappyRocking/article/details/82622881
C++代码:
class Solution {
public:
string longestPalindrome(string s) {
int p[2005]; //标记以 ss[i] 为中心的回文串半径
int id = 0, mx = 0 ;
string ss = "#" , res = "";
//插入特殊字符
for(int i = 0 ; i < s.size() ; i ++){
ss += s[i] ;
ss +="#" ;
}
for(int i = 0 ; i < ss.size() ; i ++){
if(i >=mx)
p[i] = 0 ;
else
p[i] = min(mx - i , p[2*id - i]) ;
if(i >= mx || p[i] == mx - i){ //中心扩展法向外扩充
while(ss[i + p[i]] == ss[i - p[i]] && i + p[i] + 1 < ss.size() && i - p[i] - 1 > -1)
p[i] ++ ;
}
//由于判断是否越界问题,可能导致p[i]多加1,判断后减1
if(ss[i + p[i]] != ss[i - p[i]]) p[i]--;
//更新最长回文子串中心值与最后位置的值
if(p[i] > mx - id){
mx = i + p[i] ;
id = i ;
}
}
//输出
for(int i = 2*id - mx; i <= mx ; i ++){
if(ss[i] != '#')
res += ss[i] ;
}
return res ;
}
};