《算法竞赛进阶指南》 第二章 Acwing 139. 回文子串的最大长度
地址 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;
}
第二种方法 manacher代码
#include <iostream>
#include <string.h>
using namespace std;
int n, m, p[2000010];
char s[1000010], t[2000010];
int manacher() {
memset(t,0,sizeof t);
memset(p,0,sizeof p);
n = strlen(s + 1);m = 0;
t[++m] = '#';
for (int i = 1; i <= n; i++) {t[++m] = s[i]; t[++m] = '#';}
int M = 0, R = 0;
for (int i = 1; i <= m; i++) {
if (i > R)
p[i] = 1;
else
p[i] = min(p[2 * M - i], R - i + 1);
while (p[i] <i && i+p[i] <= m && t[i - p[i]] == t[i + p[i]])
++p[i];
if (i + p[i] - 1 > R)
M = i, R = i + p[i] - 1;
}
int ans = 0;
for (int i = 1; i <= m; i++) ans = max(ans, p[i]);
//printf("%d\n",ans-1);
return ans-1;
}
int idx;
int main(){
while(scanf("%s",s+1)){
if( *(s+1)=='E'&& *(s+2)=='N'&&*(s+3)=='D') break;
int res=manacher();
printf("Case %d: %d\n",++idx,res);
}
return 0;
}
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2019-05-18 AcWing 803. 区间合并
2019-05-18 AcWing 801. 二进制中1的个数
2019-05-18 AcWing 800. 数组元素的目标和
2019-05-18 AcWing 799. 最长连续不重复子序列
2019-05-18 AcWing 795. 前缀和
2019-05-18 AcWing 791. 高精度加法 解题记录
2017-05-18 游戏脚本编程 文本token解析