求字符串中最长的回文字符串
描述
最长回文子串问题:给定一个字符串,求它的最长回文子串长度
暴力枚举
备忘录法
动态规划
分析
-
设置一个二维的数组f.
-
f[i][j]
表示区间[i,j]
是否为回文串.则状态转移方程为f[i][j]
= true (i=j)
S[i] ==S[j] (j=i+1)
S[i]==S[j] &&
f[i+1][j-1]
(j>i+1)
代码实现:
class Solution {
public:
string longestPalindrome(string s) {
// new 二维数组
int **dp = new int*[s.size()];
for (int i = 0; i < s.size(); i++) {
dp[i] = new int[s.size()];
}
for (int i = 0; i < s.size(); i++) {
for (int j = 0; j < s.size(); j++) {
dp[i][j] = 0;
}
}
int left = 0, right = 0, len = 0;
for (int i = 0; i < s.size(); ++i) {
for (int j = 0; j < i; ++j) {
// 这个循环里 i>j,所以判断
if (i == j + 1) {
dp[j][i] = s[i] == s[j];
} else { // i>j+1
dp[j][i] = s[i] == s[j] && dp[j + 1][i - 1];;
}
// 更新
if (dp[j][i] && len < i - j + 1) {
len = i - j + 1;
left = j;
right = i;
}
}
dp[i][i] = 1;
}
return s.substr(left, right - left + 1);
}
};
Manacher 算法
-
由于回文分为偶回文(比如 bccb)和奇回文(比如 bcacb),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,具体做法是:在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里)。
-
s="abbahopxpo"
,转换为s_new="$#a#b#b#a#h#o#p#x#p#o#"
长度都变成了奇数
-
通过使用个辅助数组
int p[]
,来表示 以i
为中心的最长回文半径.i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 s_new[i] $ # a # b # b # a # h # o # p # x # p # p[i] 1 2 1 2 5 2 1 2 1 2 1 2 1 2 1 4 1 2 1 -
p[i] - 1
正好是原字符串中最长回文串的长度(包含自身字符在内) -
若P[i]不包含自身的来算半径的话.
p[i]
就是最长回文串的长度.
重点就是求解 p 数组
设置两个变量,mx 和 id 。mx 代表以 id 为中心的最长回文的右边界,也就是mx = id + p[id]
假设我们现在求p[i]
,也就是以 i 为中心的最长回文半径,如果i < mx
,如上图,那么:
p[i] = (right > i) ? min(p[i_mirror], right - i) : 1;
2 * id - i
为 i 关于 id 的对称点,即上图的 j 点,
而p[j]表示以 j 为中心的最长回文半径,因此我们可以利用p[j]
来加快查找。
代码实现:
class Solution {
public:
string longestPalindrome(string s) {
// 前处理
string t = "^";
for (auto c : s) {
t += "#";
t += c;
}
t += "#";
// 开始计算
int n = t.size();
int *p = new int[n];
int resCenter = 0;
int resLen = 1;
int center = 0;
int right = 0;
for (int i = 0; i < n; i++) {
int i_mirror = 2 * center - i;
p[i] = (right > i) ? min(p[i_mirror], right - i) : 0;
while (t[i + p[i] + 1] == t[i - p[i] - 1]) {
p[i]++;
}
if (p[i] > right - center) {
right = p[i] + i;
center = i;
}
if (resLen < p[i]) {
resCenter = i;
resLen = p[i];
}
}
return s.substr((resCenter - resLen) / 2, resLen);
}
};