AcWing:139. 回文子串的最大长度(字符串Hash + 前缀和 + 后缀和 + 二分)
如果一个字符串正着读和倒着读是一样的,则称它是回文的。
给定一个长度为N的字符串S,求他的最长回文子串的长度是多少。
输入格式
输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个小写字符的形式给出。
输入以一个以字符串“END”(不包括引号)开头的行表示输入终止。
输出格式
对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。
每个输出占一行。
输入样例:
abcbabcbabcba
abacacbaaaab
END
输出样例:
Case 1: 13
Case 2: 6
算法: 字符串Hash + 前缀和 + 后缀和 + 二分
注意:其中的二分mid的取值是 (l + r + 1) >> 1,这个是有含义的,我之前写了一些博客,可供参考 ——> https://www.cnblogs.com/buhuiflydepig/p/11291541.html
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int maxn = 1e6+5; const int P = 13; ll prefix[maxn], suffix[maxn]; //前后缀和 char str[maxn]; ll p[maxn]; void init() { p[0] = 1; for(int i = 1; i <= 1000000; i++) { p[i] = p[i - 1] * P; //P进制的值位 } } void setFix(char *s) { int len = strlen(s + 1); prefix[0] = suffix[len + 1] = 0; //注意,这里需要初始话 for(int i = 1; i <= len; i++) { prefix[i] = prefix[i - 1] * P + s[i] - 'a' + 1; } for(int i = len; i >= 1; i--) { suffix[i] = suffix[i + 1] * P + s[i] - 'a' + 1; } } ll Hash1(int l, int r, ll *arr) { //前缀和计算区间的Hash值 return arr[r] - arr[l - 1] * p[r - l + 1]; } ll Hash2(int l, int r, ll *arr) { //后缀和计算区间的Hash值 return arr[l] - arr[r + 1] * p[r - l + 1]; } int main() { init(); int cas = 0; while(true) { scanf("%s", str + 1); if(strcmp(str + 1, "END") == 0) { break; } setFix(str); int ans = 0; int len = strlen(str + 1); for(int i = 1; i <= len; i++) { int l = 0, r = min(i - 1, len - i); while(l < r) { int mid =(l + r + 1) >> 1; //二分长度,以i为中间点 if(Hash1(i - mid, i - 1, prefix) == Hash2(i + 1, i + mid, suffix)) { l = mid; } else { r = mid - 1; } } ans = max(ans, l << 1 | 1); //第一个二分的是奇数的回文串,所以这里需要加一 l = 0, r = min(i - 1, len - i + 1); //这里包括i while(l < r) { int mid = (l + r + 1) >> 1; if(Hash1(i - mid, i - 1, prefix) == Hash2(i, i + mid - 1, suffix)) { l = mid; } else { r = mid - 1; } } ans = max(ans, l << 1); //这里是偶数的回文串 } printf("Case %d: %d\n", ++cas, ans); } return 0; }