解法 1:
將字符串 s 反轉得到字符串 rev,再求他們的最長公共子串,再判斷該最長公共子串是否就是我們要找的最長迴文子串。
class Solution {
public:
string longestPalindrome(string s) {
if(s.length()==1) return s;//大小爲1的字符串必爲迴文串
string rev=s;//rev存放s反轉結果
string res;//存放結果
std::reverse(rev.begin(),rev.end());
if(rev==s) return s;
int len=0;//存放回文子串的長度
for(int i=0;i<s.length();i++)//查找s與rev的最長公共子串
{
string temp;//存放待驗證子串
for(int j=i;j<s.length();j++)
{
temp=temp+s[j];
if(len>=temp.length())
continue;
else if(rev.find(temp)!=-1)//在rev中找到temp
{
string q=temp;//q用來驗證temp是否是迴文子串
std::reverse(q.begin(),q.end());
if(q==temp)
{
len=temp.length();
res=temp;
}
}
else break;
}
temp="";
}
return res;
}
};
注:該方法雖然比暴力法高效,但是在查找最長公共子串的部分效率還是不夠高,所以在力扣中最後一個測試用例會超出時間限制。
解法2:
思路:
對於這道題,我們需要判斷多個子串是否是迴文子串,在判斷的過程中會有重複的字串判斷,所以爲了,減少判斷時間複雜度,我們從暴力遞歸,改造成動態規劃的形態。
首先我們定義一個二維數組dp,行和列分別代表字符串s每個字符的下標,例如dp[j][i] 代表從下標爲j到i之間的子串,是否爲迴文子串,如果是記錄下爲true,否則爲false,
這樣就可以把之前判斷過的記錄下來,當判斷dp[j-1][i+1]的時候,當s[j-1]=s[i+1]時,我們只需要判斷dp[j][i]是否爲迴文子串即可,因爲dp[j][i]之前已經判斷過,所以不用再進入j到i這個範圍內判斷j到i是否是迴文子串。
我們再用本題中的例子具體演示一下,方便理解:
當i=1, j=0時,s[j]=b,s[i]=a,所以dp[0][1]=false;
當i=2,j=0時,s[j]=b,s[i]=b,這時候我們要判斷s[0+1]=s[2-1]?顯然相等,所以dp[j][i]=true;
當i=3,j=0時,s[j]=b,s[i]=a,不相等,j++,s[1]=a=s[3],同理此時判斷s[1+1]=s[3-1]?顯然相等。
總之就是讓列表示的下標走在前面,然後用行表示的下標,判斷列之前的每一字符的位置到它之間是否迴文,其中判斷的過程一定會有重複的判斷,而這些重複的也一定是在之前的循環過程中被判斷過,所以不用再進入子串的子串中進行判斷。
class Solution {
public:
string longestPalindrome(string s) {
int length=s.length();
if(length<=1)
{
return s;
}
vector<vector<int>>res (length,vector<int>(length,0));
//先初始化二維數組(對角線)
for(int i=0;i<length;i++)
{
res[i][i]=1;
}
int start=0;//記錄最大回文子串的開始位置
int maxlength=1;//記錄最大回文子串的長度
for(int i=1;i<length;i++)//兩層for循環遍歷每一個位置到另一個位置的情況
{
for(int j=0;j<i;j++)
{
if(s[i]==s[j])
{
if(i-j<3)//如果如果兩個相等字符之間只有一個字符,那麼一定迴文
{
res[j][i]=1;
}
else
{
res[j][i]=res[j+1][i-1];
}
}
if(res[j][i])//當j到i之間的子串迴文時判斷其長度是否最大,然後更改
{
if(i-j+1>maxlength)
{
maxlength=i-j+1;
start=j;
}
}
}
}
return s.substr(start,maxlength);
}