leetcode小白刷题之旅----5. Longest Palindromic Substring

仅供自己学习

 

题目:

Given a string s, return the longest palindromic substring in s.

 

思路:

1.暴力求解,每个元素都遍历一遍,获得的每个子串都逆向遍历一边比较是否相等,RT为O(n^3)

 

2.中心扩展法。每个元素从两侧扩展,如果左右两个数相等就继续两边都扩展,否则就下一个元素再重复动作。则顺序遍历一遍,每个元素从中间向两侧遍历一遍,故RT为O(n^2)。一些细节处理。

(1. 当字符串有偶数情况,只需要多判断一次 s[right] == s[right+1]即可,如果为True则,right=right+1,之后再left-- 和 right++进行扩展。

(2. 通过记录left记录回文串的开始位置,maxlen记录回文串长度,使用s.substr(start,maxlen)返回回文串。

(3. 可以多加一个判断条件 s.size()- i <= maxlen/2,如果为True 则break。因为当这个条件满足,该字符串剩下的元素所形成的回文串长度都会小于等于 maxlen即最长回文串长度,所以后面的元素可以不用再执行算法 直接break即可。

中心扩展法代码

 1 class Solution {
 2 public:
 3     string longestPalindrome(string s) {
 4         if (s.size() < 2) return s;
 5         int n = s.size(), maxLen = 0, start = 0;
 6         for (int i = 0; i < n;) {
 7             if (n - i <= maxLen / 2) break; 
 8             int left = i, right = i;
 9             while (right < n - 1 && s[right + 1] == s[right]) ++right;
10             i = right + 1;
11             while (right < n - 1 && left > 0 && s[right + 1] == s[left - 1]) {
12                 ++right; --left;
13             }
14             if (maxLen < right - left + 1) {
15                 maxLen = right - left + 1;
16                 start = left;
17             }
18         }
19         return s.substr(start, maxLen);
20     }
21 };

 

 

3.马拉车算法。通过空间换时间,因为只需一次遍历更新后的字符串,所以RT为O(n)。其是在每个字符开头加上$,并且元素两侧加上#使奇数和偶数个元素都为奇数处理。再通过规律(1)最长子串长度为半径减1 (2)起始位置是中间位置减去半径除以2,与核心算法 p[i] = maxright > i ? min(p[2 * center - i], maxright - i) : 1; 获取每个元素的半径,后得到回文串的开始位置以及长度。

(1.每个元素左右加上#后获得数组 t ,p设为数组 t 里每个元素的半径。这里半径的意思为以该元素为中心的回文串长度的一半,例如:$#a#b#a#,这里以b为中心,其半径为b#a# = 4。4-1=3,则为 aba的回文串长度满足(1),b位置为4,(4-4)/2=0为aba回文串起始的位置满足(2).

(2. 考虑核心算法:p[i] = maxright > i ? min(p[2 * center - i], maxright - i) : 1。只有回文串出现的时候才会有maxright> i ,除了刚开始maxright为0小于 i =1,其余情况maxright = i,因为不是回文串,p[ i ]都为1,maxright= i+p[ i ] ,而 i 在循环每次都加1。

在 maxright > i  的情况下,即canter 为一个长度大于1回文串的中心时,有以下的情况

  (2.1. maxright - i>p[ J ]  ,这里的 j 为center - j = i -center 即与 i 关于center 对称的元素 j,所以这种情况下,以S[center]为中心的回文串包含以S[i]为中心的回文串子串,且必有P[ i ]=P[ j ] ,因为i ,j在回文串里面嘛,所以 j 的半径情况和 i 的相同,周围的元素都是相同的。  j =center*2 -i。

   (2.2. maxright - i <= p[ j ]。这种情况S[ j ] 为中心的回文子串就不一定完全包含在 以S[center]为中心的回文串里了。但由于 S[ i ] 的周围元素 和 以 S[ j ] 为中心,半径为P[ j ]的圆所包括的元素和以S[center]为中心的回文串所形成的交集里的元素相同,但 因为 交集之外还含在S[ j ]为中心的这个圆的元素,即P[ j ]-回文串开始位置包括的那部分元素, 不一定还和 P[ j ]-回文串结束后的那部分元素 相同,所以S[ i ]为中心的圆的半径最大为Maxright - i,受maxright限制,所以P[ i ] 暂时只能扩展为 P[ i ]=maxright - i。而maxright 限制之外的元素还得通过中心扩展比较来判断是否相同。

这样做就能减少不必要的中心扩展次数,加快RT。

 

马拉车代码:

 1 class Solution {
 2 public:
 3     string longestPalindrome(string s) {
 4         string t ="$#";
 5         for (int i = 0; i < s.size(); ++i) {
 6             t += s[i];
 7             t += '#';
 8         }
 9         int p[t.size()], center= 0, mx = 0, resCt = 0, resMx = 0;
10         for (int i = 1; i < t.size(); ++i) {
11             p[i] = maxright > i ? min(p[2 * center - i], maxright - i) : 1;//核心算法
12             while (t[i + p[i]] == t[i - p[i]]) ++p[i];  //中心扩展
13             if (maxright < i + p[i]) {  //出现回文串或回文串在扩展后执行
14                 maxright = i + p[i];  //maxright更新到回文串最右端后的一个元素,不再回文串内,即不是回文串最右端的元素
15                 center= i;             //center移动
16             }
17             if (resMx < p[i]) {  //最长回文串更新
18                 resMx = p[i];  //获取最长回文串长度的半径
19                 resCt = i;       //获取在加了#的数组中的中心元素位置
20             }
21         }
22         return s.substr((resCt - resMx) / 2, resMx - 1); 规律(1)和(2)
23     }
24 };

 


posted @ 2021-01-25 16:41  Mrsdwang  阅读(66)  评论(0)    收藏  举报