= Vain学习(C/C++ acm算法 python Linux Golang)的个人网站 =

O(n)哈希检验求最长回文子串

Posted on

求最长回文子串,大家公认的算法是Manacher,但是如果我们不想学习Mangacher,又想在 O(n) 的时间复杂度下解决该问题怎么办?


哈希算法可以帮我们做到,而且容易理解,代码还很短

举个栗子
现在有字符串 str="cbaaab",我们要求它的回文子串,那么该怎么办呢?
这里我们借用Manacher算法里的一个思想,向原字符串里插字符,使得这个字符串为奇数个,这样方便我们统一处理。
我们在这个栗子中插入'#'

原字符:c b a a a b
现字符:# c # b # a # a # a # b #


接下来:

  • 先求出现字符串的正哈希
  • 在求出现字符串的逆哈希
  • 定义最长回文串半径
  • 枚举字符串中的点
  • 检验当前点的正哈希[i-l,i][i+l,i]是否相同
  • 若相同扩大半径,反则退出循环
  • 更新最大回文半径,记录ans
  • 输出ans-1即可

推荐题目: 回文子串的最大长度

参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
using namespace std;
const int inf = 1 << 30;
const int maxn = 2e6 + 5;
typedef long long ll;
//const int N = 5005;
char s[maxn], str[maxn];
typedef unsigned long long ull;
ull h1[maxn], h2[maxn], p[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int id = 0;
while (cin >> (s + 1) && s[1] != 'E')
{
int n = strlen(s + 1);
int t = 0;
for (int i = 1; i <= n; i++)
{
str[++t] = 'z';
str[++t] = s[i];
}
str[++t] = 'z';
p[0] = 1ull;
for (int i = 1; i <= t; i++)
{
h1[i] = h1[i - 1] * 131ull + str[i] - 'a' + 1ull;
h2[t - i + 1] = h2[t - i + 2] * 131ull + str[t - i + 1] - 'a' + 1ull;
p[i] = p[i - 1] * 131ull;
}
int l, ans = 0, j = 0;
for (int i = 1; i <= t; i++)
{
l = ans;
bool fla = false;
while (h1[i] - h1[i - l] * p[l] == h2[i] - h2[i + l] * p[l] && i + l <= t && i - l >= 1)
l++, fla = true;
if (fla && i + l <= t && i - l >= 1)
--l;
ans = max(l, ans);
}
cout << "Case " << ++id << ": " << ans - 1 << endl;
}
}