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"
思路:
1.找回文字串的问题,就要以每一个字符为中心,像两边扩散来寻找回文串,这个算法的时间复杂度是O(n*n),可以通过OJ,就是要注意奇偶情况,由于回文串的长度可奇可偶,比如"bob"是奇数形式的回文,"noon"就是偶数形式的回文,两种形式的回文都要搜索,对于奇数形式的,我们就从遍历到的位置为中心,向两边进行扩散,对于偶数情况,我们就把当前位置和下一个位置当作偶数行回文的最中间两个字符,然后向两边进行搜索。start:最长回文子串的起点,maxLen:最长回文子串的终点
C++代码:
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<2)
{
return s;
}
int start=0;
int maxLen=0;
for(int i=0;i<s.size();i++)
{
searchPalindrome(s,i,i,start,maxLen);
searchPalindrome(s,i,i+1,start,maxLen);
}
return s.substr(start,maxLen);
}
void searchPalindrome(string s,int left,int right,int &start,int &maxLen){
while(left>=0&&right<s.size()&&s[left]==s[right])
{
left--;
right++;
}
if(maxLen<right-left-1)
{
start=left+1;
maxLen=right-left-1;
}
}
};
2.思路与1相同,但在一个函数中实现。定义两个变量start和maxLen,分别表示最长回文子串的起点跟长度,在遍历s中的字符的时候,我们首先判断剩余的字符数是否小于等于maxLen的一半,是的话说明maxLen无法再变长了,直接break掉。否则就要继续判断,我们用两个变量left和right分别指向当前位置,然后我们先要做的是向右遍历跳过重复项,这个操作很必要,比如对于 noon,i在第一个o的位置,如果我们以o为最中心往两边扩散,是无法得到长度为4的回文串的,只有先跳过重复,此时left指向第一个o,right指向第二个o,然后再向两边扩散。而对于 bob,i在第一个o的位置时,无法向右跳过重复,此时left和right同时指向o,再向两边扩散也是正确的,所以可以同时处理奇数和偶数的回文串,之后的操作就是更新maxLen和start了,跟上面的操作一样。
C++代码:
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<2)
{
return s;
}
int n=s.size();
int start=0;
int maxLen=0;
for(int i=0;i<n;)
{
if(n-i<maxLen/2)
{
break;
}
int left=i,right=i;
while(right<n-1&&s[right]==s[right+1])
{
right++;
}
i=right+1;
while(left>=0&&right<n&&s[left]==s[right])
{
left--;
right++;
}
if(maxLen<right-left-1)
{
start=left+1;
maxLen=right-left-1;
}
}
return s.substr(start,maxLen);
}
};
3. 用动态规划Dynamic Programming来解,我们维护一个二维数组dp,其中dp[i][j]表示字符串区间[i, j]是否为回文串,当i = j时,只有一个字符,肯定是回文串,如果i = j + 1,说明是相邻字符,此时需要判断s[i]是否等于s[j],如果i和j不相邻,即i - j >= 2时,除了判断s[i]和s[j]相等之外,dp[j + 1][i - 1]若为真,就是回文串,通过以上分析,可以写出递推式如下:
dp[i, j] = 1 if i == j
= s[i] == s[j] if j = i + 1
= s[i] == s[j] && dp[i + 1][j - 1] if j > i + 1
这里有个有趣的现象就是如果我把下面的代码中的二维数组由int改为vector<vector<int> >后,就会超时,这说明int型的二维数组访问执行速度完爆std的vector啊,所以以后尽可能的还是用最原始的数据类型吧。
C++代码:
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty())
{
return "";
}
int dp[s.size()][s.size()]={0};
int left=0,right=0,len=0;
for(int i=0;i<s.size();i++)
{
for(int j=0;j<i;j++)
{
dp[j][i]=(s[j]==s[i]&&(i-j==1||dp[j+1][i-1]==1));
if(dp[j][i]&&(len<i-j+1))
{
left=j;
right=i;
len=i-j+1;
}
}
dp[i][i]=1;
}
return s.substr(left,right-left+1);
}
};
4. 马拉车算法Manacher's Algorithm,复杂度O(n)
C++代码:
class Solution {
public:
string longestPalindrome(string s) {
string t ="$#";
for (int i = 0; i < s.size(); ++i) {
t += s[i];
t += '#';
}
int p[t.size()] = {0}, id = 0, mx = 0, resId = 0, resMx = 0;
for (int i = 0; i < t.size(); ++i) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while (t[i + p[i]] == t[i - p[i]]) ++p[i];
if (mx < i + p[i]) {
mx = i + p[i];
id = i;
}
if (resMx < p[i]) {
resMx = p[i];
resId = i;
}
}
return s.substr((resId - resMx) / 2, resMx - 1);
}
};