求最长回文子串,大家公认的算法是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; } }
|