【LeetCode刷题记录】5. 最长回文子串

题目描述:
在这里插入图片描述
题解:
一、暴力破解(时间复杂度:O(n3),空间复杂度:O(1),超出时间限制(TLE))
BF方法的思路比较简单,逐个检查所有的子字符串,看它是否是回文子串。
二、中心扩展算法
逐字符访问,以当前字符(奇数)或当前和下一字符(偶数)为中心,向两边扩展,检查是否为回文。共有n+n-1个中心。

int expend(string s, int left, int right)
{
 int L = left;
 int R = right;
 while (L >= 0 && R<s.length() && s[R] == s[L])
 {
  L--;
  R++;
 }
 return R - L - 1;
}

string longestPalindromeCenter(string s) {
 int len = s.size();
 if (len == 0 || len == 1)
  return s;
 int start = 0;
 int end = 0;
 int mlen = 0;
 for (int i = 0; i<len; i++)
 {
  int len1 = expend(s, i, i); //odd
  int len2 = expend(s, i, i + 1); //even
  mlen = max(max(len1, len2), mlen);
  if (mlen > end - start + 1)
  {
   start = i - (mlen - 1) / 2;
   end = i + mlen / 2;
  }
 }
 return s.substr(start, mlen);
}

复杂度分析:循环O(n),扩展O(n),时间复杂度O(n2);只需要恒定的内存来存储几个局部变量,空间复杂度O(1)。
二、动态规划
在这里插入图片描述

string longestPalindromeDP(string s) {
 int len = s.size();
 if (len == 0 || len == 1)
  return s;
 int start = 0;
 int sub_len = 1;
 vector<bool> tmp_v(len, false);
 vector<vector<bool>> dp(len, tmp_v);
 for (int i = 0; i < len; i++) //1&2
 {
  dp[i][i] = true;
  if (i < len - 1 && s[i] == s[i + 1])
  {
   dp[i][i + 1] = true;
   sub_len = 2;
   start = i;
  }
 }
 for (int l = 3; l <= len; l++) //3-len
 {
  for (int i = 0; i + l - 1<len; i++)
  {
   int j = i + l - 1;
   if (s[i] == s[j] && dp[i + 1][j - 1])
   {
    dp[i][j] = true;
    start = i;
    sub_len = l;
   }
  }
 }
 return s.substr(start, sub_len);
}

复杂度分析:两层循环,时间复杂度为O(n2);创建dp数组,空间复杂度为O(n2)。
优化方法二,用一维数组代替二维数组。一维数组每次循环存储上图中的一行,下次循环会存储下一行(逐元素覆盖),这样每次只要检查s[i+1]就相当于二维数组时的检查s[i+1][j-1]。

string longestPalindromeDP(string s) {
 int len = s.size();
 if (len == 0 || len == 1)
  return s;
 int start = 0;
 int sub_len = 1;
 vector<bool> dp(len, false);
 for (int j = 0; j < len; j++)
 {
  for (int i = 0; i <= j; i++)
  {
   dp[i] = (s[i] == s[j] && (j - i < 2 || dp[i + 1]));
   if (dp[i] && (j - i + 1) > sub_len)
   {
    start = i;
    sub_len = j - i + 1;
   }
  }
 }
 return s.substr(start, sub_len);
}

复杂度分析:两层循环,时间复杂度为O(n2);创建一维dp数组,空间复杂度为O(n)。
三、Manacher算法
首先,预处理,字符间插入“#”,这样做使长度总为奇数,使奇偶的情况不需要分开讨论。首尾同样添加“#”(还可以再继续添加两个表示首尾的其它符号)。
结合下图(讨论区解释)与代码即可看懂“马拉车”:
i<maxRight时:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里得到结论:p[i] = min(maxRight - i, p[mirror])。
max(i, i+p[i])>maxRight 时,当前回文子串检查完毕,更新center与maxRight。

string preProcess(string &s){
 if (s.empty())return "^$";
 string s_tmp = "^";
 int len = int(s.size());
 for (int i = 0; i < len; i++){
  s_tmp = s_tmp + "#" + s[i];
 }
 s_tmp += "#$";
 return s_tmp;
}

string longestPalindromeManacher(string s) {
 string s_new = preProcess(s);
 int len = int(s_new.size());
 vector<int> p(len, 0);
 int c = 0, right = 0;
 //Manacher
 for (int i = 1; i < len - 1; i++) {
  int i_mirror = 2 * c - i;
  if (right > i) p[i] = min(p[i_mirror], right - i);
  while (s_new[i - p[i] - 1] == s_new[i + p[i] + 1]) {
   p[i]++;
  }
  if (i + p[i] > right) {
   c = i;
   right = c + p[i];
  }
 }
 int len_max = 0, center_max = 0;
 for (int i = 0; i < len; i++) {
  if (p[i] > len_max) {
   len_max = p[i];
   center_max = i;
  }
 }
 int start = (center_max - len_max) / 2;
 return s.substr(start, len_max);
}

科学家Manacher的工作:充分利用新字符串的回文性质,计算辅助数组 p,在填写新的辅助数组 p 的值的时候,能够参考已经填写过的辅助数组 p 的值,使得新字符串每个字符只访问了一次,整体时间复杂度为O(n);创建了一维p数组,空间复杂度为O(n)。实际上此算法里可以看到中心扩展算法的影子,对比于中心扩展算法,Manacher算法用空间换时间,将此问题的时间复杂度降为线性,达到了理论上的最优。

原创文章 23 获赞 0 访问量 989

猜你喜欢

转载自blog.csdn.net/weixin_42192493/article/details/104730427