浅谈Manacher算法
Manacher
manacher是一种\(O(n)\)求最长回文子串的算法,俗称马拉车(滑稽)
直接步入正题
首先可以知道的是:每一个回文串都有自己的对称中心,相应的也有自己的最大延伸长度(可以称之为“半径”)
我们设\(rad[i]\)表示以\(i\)为中心的回文子串的半径,那么只需要知道所有的\(rad[i]\)就可以求出最长回文子串了
从\(1\)到\(n\)枚举\(i\),求解\(rad[i]\)
设当前已经求到了\(rad[k]\),设前\(k-1\)个数中\(rad[i]+i\)(即右端点)最大的数为\(mid\),令\(r=mid+rad[mid]\)
那么可以证明一个点\(i\)关于\(mid\)的对称点是\(2\times mid-i\)(两点距离公式)
opt1:如果\(k\leq r\)
方便起见,令\(j=2\times mid-k\)
1.若\(j-rad[j]>mid-rad[mid]\),即以\(j\)点为中心的最长回文子串的左端点在以\(mid\)为中心的最长回文子串的左端点右边,则\(rad[i]\)可以直接由\(rad[j]\)转移过来(因为\(2\times mid-r\)$mid$和$mid$\(r\)是对称的,所以\(k\)的半径和\(j\)的半径是一样的),即\(rad[i]=rad[j]\)
2.若\(j-rad[j]\leq mid-rad[mid]\),则\(rad[k]\)至少是\(r-k\)。接下来再暴力延伸,顺便更新\(mid\)和\(r\)
opt2:如果\(k>r\)
直接暴力延伸,顺便更新\(mid\)和\(r\)
可以发现每次暴力延伸的时候,暴力延伸多少,\(r\)也会变化多少,且\(r\)是递增的且不超过\(n\)的数
关于复杂度:
由于每一个字符最多只会被遍历一次,所以复杂度是\(O(n)\)的
实现细节:
1.由于长度为偶数的回文串对称中心并不能实际找到,所以在两个字符之间插入#。这样不会影响答案,并且可以解决这个问题
2.对于不同情况的相似之处可以合并到一起来降低码量,如op1.1的直接转移和opt1.2的转移等
3.不要忘了更新\(mid\)和\(r\)
4.注意下标
代码:
#include<bits/stdc++.h>
using namespace std;
char cString[22000005];
int iRad[22000005];
int iLen,iAns;
void read()
{
char c = getchar();
cString[0] = '|', cString[++iLen] = '#';
while(c < 'a' || c > 'z') c = getchar();
while(c >= 'a'&& c <= 'z') cString[++iLen] = c, cString[++iLen] = '#', c = getchar();
}
int main()
{
read();//读入
for(int i = 1, mid = 0, r = 0; i <= iLen; i++)
{
if(i <= r) iRad[i] = min(iRad[mid * 2 - i], r - i + 1);//opt1
while(cString[i - iRad[i]] == cString[i + iRad[i]]) ++iRad[i];//opt1.2,opt2 尝试暴力延伸
if(i + iRad[i] > r) r = i + iRad[i] - 1, mid = i;//更新mid和r
if(iRad[i] > iAns) iAns = iRad[i];//更新答案
}
printf("%d", iAns-1);
}
浅谈Manacher算法的更多相关文章
- 浅谈Manacher算法与扩展KMP之间的联系
首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解.网上解法颇多.时间复杂度也不尽同样,这里列述几种常见的解法. 解法一 ...
- 【字符串算法2】浅谈Manacher算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 字符串算法2:Manacher算法 问题:给出字符串S(限制见后)求出最 ...
- 浅谈分词算法(5)基于字的分词方法(bi-LSTM)
目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...
- 浅谈分词算法(4)基于字的分词方法(CRF)
目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...
- 浅谈分词算法(3)基于字的分词方法(HMM)
目录 前言 目录 隐马尔可夫模型(Hidden Markov Model,HMM) HMM分词 两个假设 Viterbi算法 代码实现 实现效果 完整代码 参考文献 前言 在浅谈分词算法(1)分词中的 ...
- 浅谈分词算法基于字的分词方法(HMM)
前言 在浅谈分词算法(1)分词中的基本问题我们讨论过基于词典的分词和基于字的分词两大类,在浅谈分词算法(2)基于词典的分词方法文中我们利用n-gram实现了基于词典的分词方法.在(1)中,我们也讨论了 ...
- 【字符串算法3】浅谈KMP算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 [字符串算法3]KMP算法 Part1 理解KMP的精髓和思想 其实KM ...
- 浅谈Manacher
\(Manacher\)是由一个叫做\(Manacher\)的人发明的能在\(O(n)\)时间内找出一个字符串长度最长的回文子串的算法. 由于偶回文串形如\(abba\)这样的不好找对称中心,所以我们 ...
- 浅谈Tarjan算法
从这里开始 预备知识 两个数组 Tarjan 算法的应用 求割点和割边 求点-双连通分量 求边-双连通分量 求强连通分量 预备知识 设无向图$G_{0} = (V_{0}, E_{0})$,其中$V_ ...
随机推荐
- 【Groovy】 Groovy笔记
一.简单了解Groovy Groovy简介: Groovy是基于JVM的敏捷开发语言,语法与Java类似,但更加简洁,容错性也比Java强,同时Java能非常好的契合(例如Groovy能够使用Java ...
- web攻击日志分析之新手指南
0x00 前言 现实中可能会经常出现web日志当中出现一些被攻击的迹象,比如针对你的一个站点的URL进行SQL注入测试等等,这时候需要你从日志当中分析到底是个什么情况,如果非常严重的话,可能需要调查取 ...
- RaspberryPi交叉编译环境配置-Ubuntu & wiringPi & Qt
1.配置RaspberryPi交叉编译环境: 在开发RaspberryPi Zero的过程中,由于Zero板卡的CPU的处理性能比较弱,因此其编译的性能比较弱,需要将代码在PC电脑上交叉编译完成之后再 ...
- Python测试开发必知必会-PEP
互联网发展了许多年,不仅颠覆了很多行业,还让很多职位有了更多的用武之地.产品发布迭代速度不断加快,让测试开发这个岗位简直火得不要不要的. Python语言,作为一种更接近人来自然语言的开发语言,以简洁 ...
- 【CF 482E】ELCA
题意 题解 50pts 由于这题 \(2s\),所以可以信仰一波,暴力修改.查询. 暴力修改的复杂度是 \(O(n)\),暴力查询的复杂度是 \(O(n^2)\). 但不难发现可以通过记录子树大小来优 ...
- hive建表结构
drop table dw.fct_so;create table dw.fct_so(so_id bigint comment '订单ID',parent_so_id bigint comment ...
- leetcode二刷结束
二刷对一些常见的算法有了一些系统性的认识,对于算法的时间复杂度以及效率有了优化的意识,对于简单题和中等题目不再畏惧.三刷加油
- 第二章 Vue快速入门-- 23 品牌案例-根据关键字实现数组的过滤
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- arm开发板make编译时遇到 make[2]:*** [s-attrtab] 已杀死 问题的解决方案
未验证 出现“make[2]: *** [s-attrtab] 已杀死”log 是由于内存不足 解决方案 增加swapfile 步骤如下: 1. 查看当前swapfile状态 root@ubuntu: ...
- 使用GitHub(三):使用VSCode+GitHub进行版本控制
使用GitHub(三):使用VSCode+GitHub进行版本控制 本文简单介绍使用VSCode+GitHub进行项目或者代码的版本控制.本文主要目的是对学习内容进行总结以及方便日后查阅. 详细教程和 ...