地址 https://www.acwing.com/problem/content/141/
如果一个字符串正着读和倒着读是一样的,则称它是回文的。
给定一个长度为 N 的字符串 S,求他的最长回文子串的长度是多少。
输入格式
输入将包含最多 30 个测试用例,每个测试用例占一行,以最多 1000000 个小写字符的形式给出。
输入以一个以字符串 END 开头的行表示输入终止。
输出格式
对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。
每个输出占一行。
输入样例:
abcbabcbabcba
abacacbaaaab
END
输出样例:
Case 1: 13
Case 2: 6
解答
- 前置知识 字符串哈希
对于一个长度为len的字符串str 可以使用一个数组 unsigned long long hashV[N] 来记录
1~n的 字符串的哈希值 hashV[i]=hashV[i-1]x131 + str[i]-'a'+1;
对于a到b的区间字符串的哈希值就是 hashV[b] - hashV[a]x131^(b-a+1)
那么计算出字符串的正序和逆序哈希值
以每个点展开向两旁扩展 获取哈希值 如果每个点两边的哈希值一致 那么它就是回文串记录回文串长度,返回最大值即可.
-
回文判断有aba和aabb两种,中间填充符号就可以直接划分为一种判断方式了
比如转化为 a#b#a a#a#b#b -
考虑到数据量较大 判断回文串的长度使用二分比遍历要快,否则会tle
// Ac139.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <algorithm>
#include <memory.h>
using namespace std;
/*
如果一个字符串正着读和倒着读是一样的,则称它是回文的。
给定一个长度为 N 的字符串 S,求他的最长回文子串的长度是多少。
输入格式
输入将包含最多 30 个测试用例,每个测试用例占一行,以最多 1000000 个小写字符的形式给出。
输入以一个以字符串 END 开头的行表示输入终止。
输出格式
对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。
每个输出占一行。
输入样例:
abcbabcbabcba
abacacbaaaab
END
输出样例:
Case 1: 13
Case 2: 6
*/
const int N = 2001000;
//const int N = 1001000;
unsigned long long lh[N];
unsigned long long rh[N];
unsigned long long p[N];
const int base = 131;
char str[N];
char cpStr[N];
int ans = 1;
unsigned long long GetHashVl(unsigned long long h[N], int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
unsigned long long GetHashVr(unsigned long long h[N], int l, int r)
{
return h[l] - h[r + 1] * p[r - l + 1];
}
int GetRealLen(int len, char c) {
if (c == '#') {
if (len % 2 == 0) return len;
else return (len / 2 + 1) * 2;
}
else {
if (len % 2 == 0) return len + 1;
else return (len / 2) * 2 + 1;
}
}
int main()
{
int idx = 1;
while (1) {
ans = 1;
memset(cpStr, 0, sizeof(cpStr));
memset(lh, 0, sizeof lh);
memset(rh, 0, sizeof lh);
scanf("%s", str + 1);
if (strcmp(str + 1, "END") == 0) { break; }
int len = strlen(str + 1);
memset(cpStr, 0, sizeof(cpStr));
for (int i = 0; i < len; i++) {
cpStr[i * 2 + 1] = str[i + 1];
cpStr[i * 2 + 2] = '#';
}
//计算正向逆向的哈希值的 然后遍历字节得到以遍历点为中心的最长回文长度
len = strlen(cpStr + 1) - 1;
cpStr[len + 1] = '\0';
p[0] = 1;
for (int i = 1; i <= len; i++) {
lh[i] = lh[i - 1] * base + cpStr[i] - 'a' + 1;
int j = len - i + 1;
rh[j] = rh[j + 1] * base + cpStr[j] - 'a' + 1;
p[i] = p[i - 1] * base;
}
for (int i = 1; i <= len; i++) {
int r = min(i - 1, len - i);
int l = 0;
while (l < r) {
int mid = (l + r+1) >> 1;
if (GetHashVl(lh, i - mid, i) != GetHashVr(rh, i, i + mid)) {
r = mid-1;
}
else {
l = mid ;
}
}
int realLen = GetRealLen(l, cpStr[i]);
ans = max(ans, realLen);
}
printf("Case %d: %d\n", idx++, ans);
}
return 0;
}