LeetCode刷题记录——05最长回文字串
一 题目描述
-
给定一个字符串S,找到S的最长回文子串。你可以假设S的最大长度为100;
-
示例:
-
输入:“babad” 输出:“bab” 注意“aba”也是一个答案 输入:“cbbd” 输出:“bb”
-
二 思路
-
暴力破解:
-
列举所有的字串,逐个判断是否为回文字串,保存最长的字串
-
bool IsPalindromic(string S){ //对字串进行判断 int len = S.size(); for(int i = 0;i<len/2;i++){ if(S[i] != S[len-i-1]) //对应位置 return false; } return true; } string FindLongestPalindrome(string S){ if(S == "") return ""; string ans = ""; int Max = 0; int len = S.size(); for(int i = 0;i<len;i++){ for(int j = i+1;j<len;j++){ string temp = S.substr(i,j-i+1);//subtr(i,length),从i开始,长度为length的串 if(IsPalindromic(temp) && temp.size()>Max){ //比较得到较长的串 ans = S.substr(i,j-i+1); // Max = (Max > temp.size()) ? Max : temp.size(); Max = std::max(Max,int(temp.size())); } } } return ans; } //时间复杂度为:O(n^3) //空间复杂度为:O(1)
-
-
扩展中心,回文串一定是左右对称,循环选择一个中心,进行左右扩展,判断左右字符是否相等。
-
扩展中心数量:我们可以从字符进行扩展(n),也可以从两个字符的中间进行扩展(n-1),因此一共有2n-1个中心
-
int expandAroundCenter(string s, int left, int right){ int L = left, R = right; while(L >= 0 && R < s.length() && s[L] == s[R]){ L--; R++; } return R-L-1; } string longestPalindrome(string s){ if(s.empty()) return ""; int start = 0, end = 0; for(int i = 0;i<s.length();i++){ int len1= expandAroundCenter(s,i,i); //以i为中心向左右散开 int len2 = expandAroundCenter(s,i,i+1);//以i和i+1的中间为中心,左右散开 int len = max(len1,len2); if(len > end -start){ start = i - (len-1)/2; end = i + len/2; } } return s.substr(start,end-start+1); } //时间复杂度为O(n^2) //空间复杂度为O(1)
-
-
马拉车算法(Manacher 算法)
-
线性时间查找到到最长回文子串的方法
-
我们需要将字符串先扩展为图中T的样子,即在首尾添加^和$,在中间添加#,于是就把任何长度的字符串变为了奇数长度
-
然后用P来保存从中心C扩展的最大个数,并且刚好也是去掉#之后的原字符串的总长度,例如:下标是 6 的地方,可以看到 P[ 6 ] 等于 5,所以它是从左边扩展 5 个字符,相应的右边也是扩展 5 个字符,也就是 “#c#b#c#b#c#”,去掉#之后,变成 “cbcbc”,长度刚好是5
-
于是我们的关键就是要将P数组求出来,充分利用回文串的对成特性,
-
C是对称中心,i_mirror是 i 关于C对称的位置,i_mirror = 2*C - i;R是回文串的右半径
-
利用回文的对称性,P[i] = P[i_mirror],但是这不是在任何情况下都成立
-
不成立的情况:
- P[i_mirror] 超过了R:
- 那么此时 i 的右边没有P[i_mirror]个元素,此时可以令P[i] = min( min(R-i,P[i_mirror])),就能保证,至于会不会更大,那么就需要使用扩展中心继续比较,看 P[i] 能否 ++ ;
- P [ i_mirror ] 遇到了原字符串的左边界,即i_mirror 到了第一个字符的位置,此时需要用中心扩展法向两边扩展
- i==R,此时将P[i]赋值为0,然后中心扩展
- P[i_mirror] 超过了R:
-
C和R的更新:当P[i]的右边界 > 当前的R时,就更新C和R为当前的回文串
-
string prepross(string s){ int n = s.length(); if(n == 0){ return "^$"; } string ret = "^"; for(int i = 0;i<n;i++){ (ret +="#") += s[i]; } ret += "#$"; return ret; } string longestPalindrome(string s){ string T = prepross(s); int n = T.length(); vector<int> P(n,0); int C = 0,R = 0; for(int i = 1;i<n-1;i++){ int i_mirror = 2*C - i; if(R>i) P[i] = min(R-i,P[i_mirror]); //防止超出R else P[i] = 0; //中心扩展处理三种情况 while(T[i+1+P[i]] == T[i-1-P[i]]) P[i]++; //判断是否需要更新R if(i+P[i] > R){ C = i; R = i+P[i]; } } //找出P的最大值 int maxLen = 0; int centerIndex = 0; for(int i = 1;i<n-1;i++){ if(P[i]>maxLen){ maxLen = P[i]; centerIndex = i; } } int start = (centerIndex - maxLen)/2; cout<<start<<" || "<<maxLen<<endl; return s.substr(start,maxLen); }
-
-
寻找两个串的最长公共子串
- 回文串要求正着读和反着读,是一样的。因此是不是可以把原来的字符串反转,然后寻找这两个串的最长公共子串就可以了?试一下,例如:S = “caba” , S’ = “abac”,最长公共子串为”aba“,得解
-
求最长公共子串,使用动态规划的方法
-
申请一个二维数组 arr (保存公共子串的长度)并且全部初始化为0,然后判断对应的字符串是否相等,如果相等,就做:
arr[i][j] = arr[i-1][j-1] + 1;
-
这个方法具有局限性,对于某些字符串不能判断出来,很迷!!!
-
string LongestPalidrome(string s){ if(s.empty()) return ""; string orgin = s; string reverse_s =s; reverse(reverse_s.begin(),reverse_s.end()); //将字符串倒置 cout<<reverse_s<<endl; cout<<orgin<<endl; int length = s.size(); vector<vector<int>> arr(length,vector<int>(length,0)); int maxLen = 0; int maxEnd = 0; char temp1,tempj; for(int i=0;i<length;i++){ for(int j = 0;j<length;j++){ temp1 =orgin[i]; tempj = reverse_s[j]; if(orgin[i] == reverse_s[j]){ if(i == 0 || j==0){ arr[i][i] = 1; } else{ arr[i][j] = arr[i-1][j-1] +1; } } if(arr[i][j] > maxLen){ int beforeRev = length - 1 - j; if(beforeRev + arr[i][j] - 1 == i){ maxLen = arr[i][j]; maxEnd = i; } } } } return s.substr(maxEnd-maxLen+1,maxLen+1); }
-