标题来自原神

算法概述

Manacher 算法

用途:寻找回文串,最板子的情况下用于字符串的回文子串计数

给定一个字符串 \(S\),求出它全部的回文子串

容易想到一种暴力的 \(n^{2}\) 做法,即枚举全部中心点,开双指针向两边扩展,每扩展一次就提供 \(1\) 的贡献.

事实上,对于这样的算法来说,判断偶回文串会不太方便:因为它没有中心点.

因此,我们考虑在原字符串里加入一些莫名其妙的字符,如下:

cthoiissb
c#t#h#o#i#i#s#s#b

这样做,我们发现,当枚举原字符串内的点作为中心点时,就相当于在枚举奇回文子串,当枚举特殊字符作为中心点时,就相当于在枚举偶回文子串. 我们通过上述方法完成了对奇偶子串的统一.

下面我们来考虑对进行的转移进行优化

首先我们引入一些概念:

定义回文半径表示回文串的长度除以二的值(显然,在处理之后,回文串全都变成了奇回文串,因此直接向下取整即可),我们这样定义是因为处理后的回文半径长等于处理前的回文串长度减一,如下:

cthtc   length=5
c#t#h#t#c length=4

类似地,我们还可以求出该回文串在原串中的位置:

oicthtc
o#i#c#t#h#t#c length=4
[8]

可以发现,cthtc 的对称中心 h 在原字符串中的位置为 \(5\)(以下默认下标从 \(0\) 开始),在处理后的字符串中的位置为 \(8\),而回文半径长为 \(4\),考虑到 \(8+1-4=5\),发现再多举几组也是如此的性质,即 “原字符串位置等于处理后字符串位置加一减去回文半径”,考虑到这样有点麻烦,因此在处理后的字符串前插入一个不同的字符(也是为了防止在 \(p=0\) 时访问负数下标),实际上如果保守一点的话,末尾也是需要插入特殊字符,只不过因为末尾有一个 \0,因此不需要插入. 需要注意的是,如果真的要插入的话,首位字符不能相同,否则直接就把他们两个匹配上了,会影响答案.

到现在我们的算法还是 \(n^{2}\) 的,下面我们来考虑优化这种转移.

图源

图例中的 \(T\) 表示了一种可能的字符串,下方的每一位的 \(P\) 表示了以当前位为中心的最长回文半径. 一般来说,这个 \(P\) 数组需要我们从头到尾扫一遍来求,这一点我们无从优化,我们来考虑如何才能从之前的状态跳到当前的状态.

如图,我们已经求出了一部分 \(P\) 的值,注意到图中有一个很大的 \(P_{11}=9\),我们考虑利用一下它,因此用虚线标出它的中心与左右边界. 现在我们的目标是求出 \(P_{13}\)

假如 \(P_{11}\) 左右两边对称的话,可以想到我们只需要找到需要求的点在另一边的对称点,那么对称点的 \(P\) 值一定就也是当前点的 \(P\) 值:因为回文的性质,既然对称点是回文,对称过来也一定是回文,并且因为之前求的是最大值,因此也不存在一个更大的值了,所以直接转移即可.

下面我们再来考虑另一种情况:

现在的目标是求 \(P_{15}\),按照刚才的思路,现在我们应该去找对称点 \(P_{7}\),但是我们发现此时无法直接进行转移.

刚才我们转移是建立在一个很大的回文区间 \(P_{11}=9\) 上的,因为两边的回文区间全部都在这个大的回文区间内,因此我们才能保证两边的字符是相等的. 现在区间超出了大的对称区间的范围,因此不能保证超出范围的部分是相等的,也就不能直接转移了.

不过这样的情况还是有利用价值的,考虑可以先把能保证相等的转移了,即转移到大区间的边界,剩下的没有办法,可以直接进行暴力转移,为下一次转移助力. 可以证明这样做的复杂度是 \(O(n)\) 的.

习题

P3805 模板

在 Manacher 的具体实现中,还有几点需要注意的:

  • 因为我们的算法需要找到一个最优的区间,使其能尽可能覆盖我们当前要求的 \(P\),因此我们引入一个变量 \(id\) 来表示最优区间的中心. 考虑到,最优状态发生转移时,当且仅当新状态右边界比当前的最优右边界还要靠右,因为此时能更新到的边界也更加靠右.
  • 引入的 \(mx\) 变量用于表示当前最优区间的右边界,其实是一个非必须变量,你完全可以用 \(id+p_{id}\) 来表示它
  • 模板题要求的是回文子串的长度最大值,因此函数内最下方 if() 统计答案方式有所改变. 假如要统计子串数量,仅需改成 ans+=p[i] 即可
  • 请注意上述推导中,最大长度为回文半径减一
namespace manacher{
vector<int>p;
string insert(string x){
string res="/#";
for(int i=0;i<=x.length()-1;++i){
res.push_back(x[i]);
res.push_back('#');
}
res.push_back('?');
return res;
}
int act(string x){
x=insert(x);
p.resize(x.size());
int mx=0,id=0,ans=0;
for(int i=1;i<=x.length()-1;++i){
p[i]=(mx>i)?min(p[2*id-i],mx-i):1;
while(x[i+p[i]]==x[i-p[i]]){
p[i]++;
}
if(mx<i+p[i]){
mx=i+p[i];
id=i;
}
if(ans<p[i]){
ans=p[i]-1;
}
}
return ans;
}
}

串串

眼睛看到的:字符串

脑子里想到的

一个字符串 \(S\) 由 \(s\) 拼接而成,即 \(s\) 正序和倒序首尾相接(首位共用一个字符)

abc 可以拼成 abcbabcba

现在给出 \(S\) 的前面若干部分,求长度小于给定部分的全部合法的 \(s\)

样例:abcdcb 可以是 abcd 拼成的 abcdcba 的一部分,或者 abcdcb 拼成的一部分,因此共两种

考虑分三种情况讨论:

  • 由长度超过给定字符串长度一半的字符串拼接:此种情况应至多拼接两次,因此只需要判断最长回文子串的末端是否到达右边界即可
  • 由长度不足给定字符串长度一半的字符串拼接:此种情况应拼接了多次,只需要判断当前位为回文,并且翻转后仍能翻转即可. 这一点我们可以通过改变遍历顺序来通过标记处理.
  • 可以发现其本身一定能构成一个 \(s\)

形象一点?比如给出字符串 qwqwq,它可以是由子串 qwqw 翻转而得的,而 qwqw 是由子串 qwq 翻转而得的,而 qwq 是由子串 qw 翻转而得的,然而,qw 无法由它的子串翻转而得,此时算法结束

#include<bits/stdc++.h>
using namespace std;
string x;
namespace manacher{
vector<int>p;
string insert(string x){
string res="/#";
for(int i=0;i<=x.length()-1;++i){
res.push_back(x[i]);
res.push_back('#');
}
res.push_back('?');
return res;
}
void act(string x){
p.clear();
x=insert(x);
p.resize(x.size());
int mx=0,id=0;
for(int i=1;i<=x.length()-1;++i){
p[i]=(mx>i)?min(p[2*id-i],mx-i):1;
while(x[i+p[i]]==x[i-p[i]]){
p[i]++;
}
if(mx<i+p[i]){
mx=i+p[i];
id=i;
}
}
}
}
bool vis[1000001];
int main(){
int cases;cin>>cases;while(cases--){
cin>>x;
manacher::act(x);
for(int i=x.length()*2-2;i>=2;i-=2){
vis[i/2]=false;
if((manacher::p[i]-1+i)/2==x.length()) vis[i/2]=true;
if(vis[(manacher::p[i]-1+i)/2] and manacher::p[i]==i) vis[i/2]=true;
}
for(int i=1;i<=x.length()-1;++i){
if(vis[i]){
cout<<i<<" ";
vis[i]=false;
}
}
cout<<x.length()<<endl;
}
}

[OI] 欢夏!邪龙?马拉车!的更多相关文章

  1. 天气预报API(二):全球城市、景点代码列表(“旧编码”)

    说明 2016-12-10 补充 (后来)偶然发现中国天气网已经有城市ID列表的网页...还发现城市编码有两种,暂且称中国天气网这些编码为旧标准"旧编码"的特征是 9个字符长度; ...

  2. 浅析软件工程中的UML建模技术

    一.基本信息 标题:浅析软件工程中的UML建模技术 时间:2018 出版源:电子世界 领域分类:软件工程:UML建模技术:需求分析 二.研究背景 问题定义:软件工程中UML建模技术的研究 难点:明确软 ...

  3. Ajax的初体验

    一.AJAX的介绍 Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术. Ajax =  ...

  4. 搞笑OI

    OI难 噫吁嚱,维护难哉!OI之难,难于上青天!哈希及DP,代码何茫然!尔来一千两百A,不见金牌背后难.西当华师有考场,可以横绝CN巅.编译不过壮士死,然后超时爆内存相钩连.上有自主招生之高标,下有由 ...

  5. 再见,OI

    你好,NOIP 2015年9月1日 正式成为了福建省莆田一中的一名高一成员 后来学校搞了选修 大家都很激动 因为自己的兴趣和特长能够得到发挥了(或者说能逃课或者看好多电影) 发现选修提供的选项中有好几 ...

  6. 08重编终极版《东邪西毒:终极版》DVD粤语中字

    1.东邪西毒].Ashes.of.Time.1994.384p.DVDRip.x264.ac3-DTMM.mkv 这个版本最清晰 ,可惜删减了,只有87分钟,粤语,1.4G. 2.东邪西毒(初始版). ...

  7. OI暑假集训游记

    莞中OI集训游记 Written BY Jum Leon. I        又是一载夏,本蒟蒻以特长生考入莞中,怀着忐忑的心情到了8月,是集训之际.怀着对算法学习的向往心情被大佬暴虐的一丝恐惧来到了 ...

  8. OI生涯回忆录 2017.9.10~2018.11.11

    然而并没有退役 为了这两天,也准备了一年. 高一零基础的蒟蒻,NOIP2017仅有80pts 之后看着luogu的倒计时, 300天,200天,100天,30天, 直到10天.1天. 10号,11号的 ...

  9. 写在OI退役后和高中毕业前的一些话

    更新日志: 2017.02.13 开坑 2017.02.13 更新[零][壹] 2017.02.14 更新[贰] 2017.02.26 更新[叁][肆] 2017.03.04 锅多如狗,停更一周 20 ...

  10. 我的OI生涯 第六章

    开学了,但是我们并没有像一个正常的高二学生一样坐在教室里接受调研考试的洗礼. 暑假作业这种东西早已被甩在一旁,可以想象回去补文化课时该有多么狼狈. 大王给我们制定了周密的计划,每周两次测试,加上蔡老师 ...

随机推荐

  1. Microsoft Dynamics CRM 插件被限制2分钟超时解决方案

    背景: 在隔离模式"沙箱"中运行的插件或自定义工作流活动将有2分钟的硬限制.如果你的插件很复杂,需要超过2分钟,有一些解决方法. CRM on premise (本地版) 选择插件 ...

  2. 【Scala】07 集合

    分三大类: 序列 Seq 集 Set 映射 Map 所有集合类型都扩展自Iterable特质(可迭代的) 所有集合类型都提供[可变]和[不可变]的版本 归纳在下面两个包中 scala.collecti ...

  3. douyin 今日头条 巨量登录滑块和douyin详情滑块分析

    声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容.敏感网址.数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均 ...

  4. 植物大战僵尸杂交版v2.1整合包全解锁+高清工具

    植物大战僵尸杂交版v2.1整合包全解锁+高清工具   引言 <植物大战僵尸>作为一款经典的塔防游戏,自2009年发布以来,就以其独特的游戏机制和幽默的风格赢得了全球玩家的喜爱.随着游戏的不 ...

  5. 编程语言中的Variable Shadowing(变量遮蔽)—— declaration shadows a local variable —— Consider Allow Shadowing of let Bindings

    Variable Shadowing(变量遮蔽)是编程语言中比较常见的一种情况,但是由于不同语言对于这个情景的处理是不同的,所以在具体语言中这个Variable Shadowing(变量遮蔽)的表现也 ...

  6. 决定了,今日起开始准备弃用京东JD

    估计京东是为了节约开支,然后开始大比例的把快递物流业务进行外包了,这直接导致服务质量的直线下滑,10多年前我选择弃用当当网而选择京东JD就是因为当时当地的当当网快递是用沈阳晚报的快递上门的,快递员连P ...

  7. 【转载】 CV往哪卷?李飞飞指出三颗「北极星」:具身智能,视觉推理和场景理解

    原文地址: https://news.cnblogs.com/n/720105/ 新智元报道 编辑:LRS ============================================== ...

  8. 【开启报名】同学看过来,Apache DolphinScheduler开源之夏课题任务正式发布!

    如果你还拥有着一张有效的"学生证",在这个充满机遇的夏天,我们诚邀你加入一个充满挑战和机遇的开源冒险--开源之夏. 这不仅是一个简单的编程开发活动,假如你成功参加并结项之后,还能获 ...

  9. 什么是MMU

    一.MMU的定义   MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时也称作分页内存管理单元(Paged Memory Management Unit,缩写为PM ...

  10. 安装RabbitMQ遇到的一些坑

    Ubantu18.0正确安装RabbitMQ 1.安装erlang 因为RabbitMQ需要erlang语言的支持,所以我们需要先安装erlang. sudo apt-get install erla ...