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 };

浙公网安备 33010602011771号