题目 leetcode:5.?Longest Palindromic Substring 解法 动态规划 时间复杂度 \(O(n^2)\) ,空间复杂度 \(O(n^2)\) 基本解法直接看代码 class Solution {public: string longestPalindrome(string s) { int n = s.size(); vecto
题目
leetcode:5.?Longest Palindromic Substring
解法
动态规划
时间复杂度\(O(n^2)\),空间复杂度\(O(n^2)\)
基本解法直接看代码
class Solution { public: string longestPalindrome(string s) { int n = s.size(); vector<vector<bool>> dp(n, vector<bool>(n, true)); int rx, ry; rx = ry = 0; for(int l = 1; l < n; l++){ for(int i = 0; i < n - l; i++){ int j = i + l; if(s[i] == s[j] && (j - i < 3 || dp[i+1][j-1])){ dp[i][j] = true; if(j - i > ry - rx){ ry = j; rx = i; } } else { dp[i][j] = false; } } } return s.substr(rx, ry - rx + 1); } };
中心扩散法
时间复杂度\(O(n^2)\),空间复杂度\(O(1)\)
我们先假定以某点为中心向两端扩散,找到以该点为中心的最长回文子串
class Solution { public: int rx, ry; void helper(string &s, int i, int offset){ int left = i; int right = i + offset; while(left >= 0 && right < s.size() && s[left] == s[right]){ left--; right++; } if(right - 1 - (left + 1) > ry - rx){ ry = right - 1; rx = left + 1; } } string longestPalindrome(string s) { int n = s.size(); rx = ry = 0; for(int i = 0; i < n; i++){ helper(s, i, 0); helper(s, i, 1); } return s.substr(rx, ry - rx + 1); } };
Manacher算法
Manacher算法俗称“马拉车算法”,时间复杂度\(O(n)\),空间复杂度\(O(n)\)
因为回文字符串都有奇数长度的串和偶数长度的串,为了更好处理这两种情况,可以在字符串中插入一特殊字符‘#‘,使得新字符串长度全变为奇数长度,如"aa"变为"#a#a#",可以再字符串首部加入另一特殊字符‘$‘和尾部的‘@‘,这样就不用特殊处理越界问题(统一边界处理)
以"122112321"为例经过上一步变成"@#1#2#2#1#1#2#3#2#1#"
Manacher算法使用一个辅助数组r[i]表示以t[i]为中心的最长回文子串的最右字符到t[i]的长度,如以t[i]为中心的最长回文子串为t[low, high],则r[i] = high - i + 1, t[low, high] = 2 * r[i]-1, len数组有一个性质,就是r[i]-1为该回文子串在原串中的长度,证明很简单t[lowl, high]一定是以"#"开头和结尾的,这样插入的"#"是原来串中字符的两倍还多一个,这样原串中最长回文串的长度就为r[i]-1,这样问题就转为求最长的r[i]
计算len数组
算法主要利用了已有的回文子串的特点,减少了查找时间,从左往右计算len[i],同时保存一个之前计算最长回文子串的右端点的最大值R及对应的中心位置c,
- i < R, 则先找i关于c对称点j=2*c-i,则至少r[i] \(\geq\) min(R - i + 1, p[j]), 超出部分再手工匹配
- i >= R, 则不能利用以后的知识做任何假设,只能假定其至少为1,再手工匹配
class Solution { public: string longestPalindrome(string s) { int n = s.size(); if(n == 0) return ""; string ns; ns.push_back('$'); for(int i = 0; i < n; i++){ ns.push_back('#'); ns.push_back(s[i]); } ns.push_back('#'); ns.push_back('@'); n = ns.size(); vector<int> r(n); int c, R, C, MAX; R = -1; MAX = 0; C = 0; for(int i = 1; i < n; i++){ r[i] = i < R ? min(r[2 * c - i], R - i + 1) : 1; while(ns[i + r[i]] == ns[i - r[i]]) r[i]++; r[i]--; if(i + r[i] > R){ R = i + r[i]; c = i; } if(r[i] > MAX){ MAX = r[i]; C = i; } } return s.substr((C-MAX)/2, r[C]); } };
时间复杂度分析
参考
- Manacher‘s ALGORITHM
- Leetcode: Longest Palindromic Substring 最长回文子字符串