暴力:(力扣过不了)
直接两层 for 循环遍历穷举所有区间,判断所有区间是否是回文
用了两种暴力法:
第一种、通过左右指针判断是否相等,不相等就返回false
第二种、通过字符串反转,因为回文的特性,左右、反转都相等
需要注意的边界问题:
1、是当字符串小于 2 时就是回文了,直接返回
2、大于 2 时,最小的回文长度是 1
3、字串的长度是 j - i + 1
class Solution {
public:
string longestPalindrome(string s) {
int len = s.size();
if (len < 2) return s;
int index = 0;
int maxLen = 1;
for (int i = 0; i < len - 1; ++i) {
for (int j = i + 1; j < len; ++j) {
// std::string tmp = s.substr(i, j - i + 1);
// std::reverse(tmp.begin(), tmp.end());
// if (s.substr(i, j - i + 1) == tmp) {
if (judgePalindrome(s, i, j)) {
int tmp_len = j - i + 1;
if (tmp_len > maxLen) {
maxLen = tmp_len;
index = i;
}
}
}
}
return s.substr(index, maxLen);
}
bool judgePalindrome(string s, int left, int right) {
while (left < right) {
if (s[left++] != s[right--]) {
return false;
}
}
return true;
}
};
时间复杂度:,显而易见,三个循环
空间复杂度:,常量个变量
动态规划:(对暴力求解进行优化)
因为暴力求解的过程中其实有大量的重复验证的工作,当验证 i 到 j 这一段字符串是否是回文时,如果我们提前验证了 i + 1 到 j - 1 这一段,
并且是回文串,那么我们只需要判断 s[i] 和 s[j] 就可以了知道 i 到 j 这一段是不是回文串了,这其实是一个状态转移的过程。
状态转移方程:
对于长度小于 3 的字符串,我们需要特殊处理,因为长度为 1 就是回文,长度为 2 只需要比较 s[i] 和 s[j],不需要状态转移
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if (len < 2) return s;
int index = 0;
int max_len = 1;
vector<vector<bool>> dp(len, vector<bool>(len, false));
for (int l = 0; l < len; ++l) {
for (int i = 0; i < len - l; ++i) {
int j = i + l;
if (0 == l) {
dp[i][j] = true;
}
else if (1 == l) {
dp[i][j] = (s[i] == s[j]);
}
else {
dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]);
}
if (dp[i][j] && j - i + 1 > max_len) {
max_len = j - i + 1;
index = i;
}
}
}
return s.substr(index, max_len);
}
};
代码中,第一个 for 循环 l 表示回文串的长度,从 0 开始,依次到最大长度减二,这里用 l == 0 表示长度为 1,l == 1 表示长度为 2,这里的 l 可以理解为 i 和 j 的间距,0 个间距自然只有一个字符,一个间距就有两个字符
时间复杂度:,显而易见,两个循环
空间复杂度:,用到了 dp,这是以空间换时间的算法
中心扩展算法:
马拉车算法(Manacher):