问题描述:给定一个字符串,找到字符串中的最长回文子串。
思路:
- 暴力求解
描述:枚举所有子串,判断是否为回文子串,并设置全局最大长度回文子串,在遍历过程中不断更新。
时间复杂度:o(n3)
空间复杂度:o(n)
- 最长公共子串(二维打表)
描述:设置二维表记录原字符串与反转字符串对比情况,在比较过程中动态的记录回文子串的长度。
时间复杂度:o(n2)
空间复杂度:o(n2)(可通过一维复用达到o(n))
- 动态规划
描述:与上面方法类似,设置二维表,但只用右上角部分,从主对角线往上分别表示长度为1…n的情况,其中1,2需初始化。
时间复杂度:o(n2)
空间复杂度:o(n)或o(n2)
- 中心扩展算法
描述:考虑奇偶情况的2n - 1个位置(每个元素和元素之间的间隙位置),以每个位置作为中心点扩散,遍历找到最大子串。
时间复杂度:o(n2)
空间复杂度:o(1)
- Manacher(马拉车算法)
描述:对中心扩展算法的优化,通过补充无关元素来达到统一奇偶的目的。添加一额外数组记录以该元素为中心的回文串长度,于是问题转变为如何更好地得出辅助数组。具体思想是通过回文映射,将问题分为三类情况
原字符串:a b c b d
变化后:
1. 正常情况
$ c $ a $ b $ a $ c $ b $
0 1 0 1 0 3
j’ C j R//j为遍历位置,C为中心,R为中心扩展边界
2.遍历位置加映射值的下标大于R
3.j’遇到左边界,由于j’扩展时受限,不能确定j的情况
4.j=R
除第一种情况外,后面三种都要使用中心扩展算法进行扩展
时间复杂度:o(n)
空间复杂度:o(n)
//暴力法(略)
//最长公共子串
class Solution {
public:
string longestPalindrome(string s) {
string str = s;
string maxstr = "";
int maxlen = 0;
int index;
int len = s.length();
vector<vector<int>> var(len + 1, vector<int>(len + 1));
if (len == 1) return str;//字符串长度为1,直接返回
reverse(str.begin(), str.end());//将字符串反转,准备进行比较
if (str == s) return s;
for (int i = 0; i < len + 1; ++i){
for (int j = 0; j < len + 1; ++j){
if (i == 0 || j == 0){
var[i][j] = 0;//初始化二维表首行首列
}
else if (str[i - 1] == s[j - 1]){
var[i][j] = var[i - 1][j - 1] + 1;//若反转前后元素相同,值更新为左上角值加一
if (var[i][j] > maxlen && (i + j - var[i][j]) == len){
maxlen = var[i][j];//若记录值超过最大记录长度,更新最大长度
index = j;记录子串尾下标
}
}
else{
var[i][j] = 0;
}
}
}
if (maxlen){
int begin = (index - maxlen) > 0 ? (index - maxlen):0;//找到最大回文子串的首下标
maxstr = s.substr(begin, maxlen);
}
return maxstr;
}
};
//动态规划
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
int index, end, max = 1;
if(len == 1 || len == 0) return s;
vector<vector<bool>> dp(len, vector<bool>(len));//构建二维数组
for(int i = 0; i < len; ++i){
dp[i][i] = 1;//单个元素必是回文
if(i < len - 1 && s[i] == s[i+1]){
dp[i][i+1] = 1;//两个元素回文判断
max = 2;
index = i;
}
}
for(int l = 3; l <= len; ++l){//长度大于3的情况
for(int start = 0; start + l - 1 < len; ++start){
end = start + l - 1;
if(dp[start + 1][end - 1] && s[start] == s[end]){//如果长度大的子串去掉两端未回文串且两端元素相同,二维表对应值置1
dp[start][end] = 1;
max = l;//由于越往后判断长度越大,所以更新值一定是最大值
index = start;
}
}
}
return s.substr(index, max);
}
};
//优化空间复杂度至o(n)
/*
a b c b d
1 //i = 4, j = 4
1 0 //i = 3, j = 4, 3
1 0 0 //i = 2, j = 4, 3, 2
1 0 1 0 //i = 1, j = ...,(当j = 3时需要用到上一步i=3,j=3的信息)
1 0 0 0 0//...
*/
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
string maxstr = "";
vector<bool> dp(len);//采用一维数组记录
for(int start = len - 1; start >= 0; --start){//从最后一位字符开始
for(int end = len - 1; end >= start; --end){
dp[end] = s[start] == s[end] && (end - start < 2 || dp[end - 1]);
if(dp[end] && end - start + 1 >= maxstr.length()){
maxstr = s.substr(start, end - start + 1);
}
}
}
return maxstr;
}
};
//中心扩展算法
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
string maxstr = "";
for(int i = 0; i < len; ++i){
int len1 = expendCenter(s, i, i);
int len2 = expendCenter(s, i, i + 1);
int maxlen = max(len1, len2);//找出相邻奇偶最大回文串长度
if(maxlen > maxstr.length()){
int left = i - (maxlen - 1)/2;//找到最大回文串左下标
maxstr = s.substr(left, maxlen);
}
}
return maxstr;
}
private:
int expendCenter(string s, int i, int j){
int L = i;
int R = j;
while(L >= 0 && R <= s.length() && s[L] == s[R]){//回文扩散
--L;
++R;
}
return R - L - 1;
}
};
//manacher算法
class Solution {
public:
string longestPalindrome(string s) {
int len = s.length();
if(len == 0 || len == 1) return s;
string s1;
for(int i = 0; i < len; ++i){//补位统一奇偶
s1 += '#';
s1 += s[i];
}
s1 += '#';
int len1 = s1.length();
int jmap = 0;
int C = 0;
int R = 0;
int maxlen = 1;
int start = 0;
vector<int> p(len1, 0);
for(int j = 0; j < len1; ++j){
if(j < R){
jmap = 2 * C - j;//当前遍历位置关于C的映射
p[j] = min(R - j, p[jmap]);//防止j+p[jmap]>R的情况
}
int left = j - p[j] - 1;//中心扩展左端点
int right = j + p[j] + 1;
while(left >= 0 && right <= len1 && s1[left] == s1[right]){//应对后三种情况进行中心扩展
++p[j];
--left;
++right;
}
if(j + p[j] > R){//对R和C进行更新,保证j一直处于辐散区域
R = j + p[j];
C = j;
}
if(p[j] > maxlen){//维持最大回文子串,对应原数组
maxlen = p[j];
start = (j - maxlen)/2;
}
}
return s.substr(start, maxlen);
}
};