给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
一.蛮力法
遍历该字符串所有子串,该过程时间复杂度为O(n2),判断该字符串是否为回文串,该过程时间复杂度O(n),过程略
class Solution {
public:
bool isPalindrome(string s){
int i=0,j=s.length()-1;
while(i<j)
{
if(s[i]!=s[j])
return false;
i++;j--;
}
return true;
}
string longestPalindrome(string s) {
int max=0;
string ans;
for(int i=0;i<s.length();i++)
{
int t=i;
while(t<s.length())
{
if(s[i]==s[t]&&(t-i+1)>max)
{
string str(s,i,t-i+1);
if(isPalindrome(str))
{
ans=str;
max=t-i+1;
}
}
t++;
}
}
return ans;
}
};
二.动态规划
为了改进暴力法,我们首先观察如何避免在验证回文时进行不必要的重复计算。考虑 “ababa”\textrm{“ababa”}“ababa” 这个示例。如果我们已经知道 “bab”\textrm{“bab”}“bab” 是回文,那么很明显,“ababa”\textrm{“ababa”}“ababa” 一定是回文,因为它的左首字母和右尾字母是相同的。
我们给出 P(i,j)P(i,j)P(i,j) 的定义如下:
P(i,j)={true,如果子串Si…Sj是回文子串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 (Si==Sj) 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)=true P(i, i) = true P(i,i)=true
P(i,i+1)=(Si==Si+1) P(i, i+1) = ( S_i == S_{i+1} ) P(i,i+1)=(Si==Si+1)
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n−12n - 12n−1 个这样的中心。
你可能会问,为什么会是 2n−1个,而不是 n 个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
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 = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}