LeetCode 5
Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
Example 1:
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
Soulution:
static const auto speedup = [](){ ios::sync_with_stdio(false); cin.tie(nullptr); return nullptr; }(); class Solution { public: string pre (string s){ string t = "#"; for (int i = 0; i < s.size(); i++){ t += (s.substr(i,1) + "#"); } return t; } string after (string s, int id, int num){ int start = 0; if(num % 2 == 1){ start = (id - 1) / 2 - (num / 2 - 1); num--; }else{ start = id / 2 - (num / 2 - 1); num--; } return s.substr(start, num); } string longestPalindrome(string s) { if(s == "")return s; int mx = 0, center = 0, target_num = 0, target_id = 0; string t = s; s = pre(s); vector<int> P(s.size()); P[0] = 1; for (int i = 1; i < s.size(); i++){ P[i] = i < mx ? (P[2*center-i] < (mx - i) ? P[2*center-i] : (mx - i)) : 1; while(i + P[i] < s.size() && i - P[i] >= 0 && s[i+P[i]] == s[i-P[i]]){ P[i]++; } if(i + P[i] > mx){ center = i; mx = i + P[i]; } if(P[i] > target_num){ target_id = i; target_num = P[i]; } } return after (t, target_id, target_num); } };
1.基本思路
1.1 Manacher's Algorithm
简单来说这个算法是利用回文串镜像对称的特点,使在检测未探测到的回文串时,利用已知回文串的左半部分,尽可能地使得判断他右边的字符串的探测次数变少。从而达到O(n)的时间复杂度和O(n)的空间复杂度。
首先将要探测的字符串插入#字符(包含头尾),这是为了让你总能找到一个在数组中确实存在的对称中心,无论是奇数还是偶数个的字符串。
其中最核心的一行代码是
P[i] = i < mx ? (P[2*center-i] < (mx - i) ? P[2*center-i] : (mx - i)) : 1;
其中的变量如下:
P 存储该对称中心下回文字符串可以延伸的长度,P[i]-1为该长度
i 目前判断的index
mx 为当前延伸到最右端的回文串的最右端index,为的是尽可能大的利用回文串的镜像对称特性
center 为当前延伸到最右边的回文串的对称中心
所以对上面公式理解如下:
1 当i > mx时,意味着当前判断的字符串中心位置,已经超出之前可以延伸到最右端的回文串的最右端,这时候只能重新开始正常判断
2 当i <= mx时,就是当前判断的中心在字符串中,可以利用镜像对称的特性进行初始值的设定
2.1 (P[2*center-i] < (mx - i) ? P[2*center-i] : (mx - i)) 这里的P[2*center-i]是与当前中心沿着Center对称的中心字符串的最长回文串长度,当时如果超过了中心回文串右边界的长度mx-i,那么大于mx-i的部分就没有意义了,因为在这一部分无法利用镜像对称对P进行初始化
3 当i+P[i]>mx时,即当前回文串的最右端超过上一个回文串的最右端,那么更新参数center和mx,使得我们可以充分利用镜像的特性
4 同时对比当前的最大长度,实时更新最大长度和它对应的index
1.2 动态规划 DP
const int maxn = 1010; char a[maxn]; int dp[maxn][maxn]; int len = 0, ans = 1; void init() memset(dp, 0, sizeof(dp)); for(int i = 0; i < len; ++i){ dp[i][i] = 1; if(i < len - 1){ int j = i + 1; if(a[i] == a[j]){ dp[i][j] = 1; ans = 2; } } } } void DP(){ init(); for(int l = 3; l < len; ++l){ for(int i = 0; i + l - 1 < len; ++i){ int j = i + l - 1; if(a[i] == a[j] && dp[i + 1][j - 1] == 1){ dp[i][j] = 1; ans = l; } } } }