题目描述
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
解题思路
- 暴力法:利用
两个for循环
,找出所有可能种回文子串,每次找出一个回文子串时与前一个进行比较选择回文子串长度大的作为返回的结果 - 回文:
翻转过后保持不变
,例如回文数:121,1221,13231,回文子串:“abcba”,“bcb”
Cpp实现
class Solution {
public:
string longestPalindrome(string s) {
string res = "";//利用res存放结果
string temp = "";//利用temp存放循环过程中的子串
for(int i = 0; i < s.size();i++){
for(int j = i; j < s.size();j++){
temp = temp + s[j];
string _temp = temp;
std::reverse(_temp.begin(),_temp.end());
if(_temp == temp)
res = res.size() > temp.size() ? res : temp;
//一个字符串中可能存在多个回文子串
//我们需要找到最长的回文子串
}
temp = "";//循环完成一次后把temp转化成空字符串,再次进入循环去记录过程中给子串
}
return res;
}
};
- 当字符串长度很长时,提交会超出时间限制
- 时间复杂度:O(n^3)
Python实现
- 利用Python
切片
方法
class Solution:
def longestPalindrome(self, s: str) -> str:
if s == s[::-1]:
return s
max_len = 1
res = s[0]
for i in range(len(s) - 1):
for j in range(i + 1,len(s)):
if j - i + 1 > max_len and s[i:j + 1] == s[i:j + 1][::-1]:
max_len = j - i + 1
res = s[i:j + 1]
return res
- 时间复杂度:O(n^2)l
解题思路
- 动态规划:定义一个二维数组
bool dp[len-1][len-1]
来记录遍历字符串所得的状态,dp[l][r]
为true
表示从l到r
的子串为回文串,false表示不是回文串 - 初始化二维数组,单个字符为回文串,所以定义
dp[i][i]=true
- 找到状态转移方程:
dp[l][r] = (s[r] == s[l]) && (r - l == 1 || dp[l+1][r-1]) ? true : false
Cpp实现
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if (len == 0)
return s;
bool dp[len][len];
int start = 0, end = 0;
for (int i = 0; i <len; i++)
dp[i][i] = true;
for (int r = 1; r < len; r++)
for (int l = 0; l < r; l++)
if (s[r]==s[l] && (r-l==1 || dp[l+1][r-1])) {
dp[l][r] = true;
if (r-l > end-start) {
start = l; end = r;
}
continue;
}else
dp[l][r] = false;
return s.substr(start, end-start+1);
}
};
- 时间复杂度:O(n^2)
Java实现
- 状态:
dp[i][j]
:表示子串s[i,…,j]是否为回文子串 - 状态转移方程:
dp[i][j] = (s[i] == s[j]) and dp[i +1][j - 1]
- 边界条件:
(j - 1) - (i + 1) + 1 < 2
,整理得:j - i < 3时
,即无法构成区间,相当于s[i,....,j]
长度为2或者3时,不用检查子串是否回文 - 初始化:
dp[i][i]
= true ,即对角线上的为true(一个字符一定是回文串) - 输出:在得到一个状态的值为true的时候,记录起始位置和长度,填表完成后再截取
class Solution {
public static String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
//初始化二维表
//dp[i][j]表示s[i...j]是否为回文串
boolean[][] dp = new boolean[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
//注意:左下角先填,因为 每一个dp[i][j] 需要参考dp[i + 1] [j - 1] 即为左下角
//一列一列的填,只需要填上三角,且对角线已经赋值,只需要填对角线上方的值即可
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
}else{
//头尾去掉没有字符或剩下一个字符的时候
if (j - i < 3) {
dp[i][j] = true;
} else {
//参考中间部分的答案
dp[i][j] = dp[i + 1][j - 1];
}
}
//只要dp[i][j] = true成立,就表示子串s[i...j]是回文,记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
- 时间复杂度:O(n^2)
解题思路
马拉车算法
:如果字符串是奇数个,我们通过遍历所有字符串,再对所有字符串进行左右匹配,然后得到长度最大的字符串,如果字符串是偶数个,我们重新构造新的字符串即可- 学习一下马拉车算法并进行优化,学习一下别人写的代码,实在是太厉害了
Cpp实现
class Solution {
public:
string longestPalindrome(string s) {
//插入"#"
string t="$#";
for(auto c:s){
t+=c;
t+='#';
}
vector<int> p(t.size(),0);
//mx表示某个回文串延伸在最右端半径的下标,id表示这个回文子串最中间位置下标
//resLen表示对应在s中的最大子回文串的半径,resCenter表示最大子回文串的中间位置
int mx=0,id=0,resLen=0,resCenter=0;
//建立p数组
for(int i=1;i<t.size();++i)
{
//更新p[i]的值
p[i]=mx>i?min(p[2*id-i],mx-i):1;
//遇到三种特殊的情况,需要利用中心扩展法
while(t[i+p[i]]==t[i-p[i]])p[i]++;
//半径下标i+p[i]超过边界mx,需要更新
if(mx<i+p[i]){
mx=i+p[i];
id=i;
}
//更新最大回文子串的信息
if(resLen<p[i]){
resLen=p[i];
resCenter=i;
}
}
//最长回文子串长度为半径-1,起始位置为中间位置减去半径再除以2
return s.substr((resCenter-resLen)/2,resLen-1);
}
};
- 时间复杂度:O(n)