浅谈字符串算法(KMP算法和Manacher算法)

【字符串算法1】 字符串Hash(优雅的暴力)

【字符串算法2】Manacher算法

【字符串算法3】KMP算法

这里将讲述  字符串算法2:Manacher算法

问题:给出字符串S(限制见后)求出最大回文子串长度

Subtask1  对于10%的数据 |S|∈(0,100]

Subtask2  对于30%的数据|S|∈(0,5000]

Subtask3 对于100%的数据|S|∈(0,11000000]

Subtask1(10pts):最朴素的暴力 枚举字符串的所有子串,判断其是否回文,时间复杂度是O(n3)的

Subtask2(30pts):利用回文串的性质:  所有的回文串都是对称的。

长度为奇数回文串以最中间字符的位置为对称轴左右对称,

而长度为偶数的回文串的对称轴在中间两个字符之间的空隙。

可以遍历这些对称轴,

在每个对称轴上同时向左和向右扩展,直到左右两边的字符不同或者到达边界。

时间复杂度O(n2

Subtask3(100pts):?(当然是Manacher算法囖)O(n)

改进优化 Subtask2(30pts) :

1.麻烦程度(预处理字符串)

在每两个字符中间插入另一个字符,如‘#‘。

也就是说,原来的字符串是这样的

现在的字符串是这样的(为了不越界以及方便,a[0]设置为‘#’)

那么这个缺点解决了(现在不用分奇数偶数讨论了qwq)

【图是哪里偷过来的,足以说明问题】

2.会出现很多子串被重复多次访问,时间效率大幅降低。

要想降维 O(n)的做法是每一位只扫一次或常数次这里每一位都扫了将近n次

------------------(正文分割线)----------------------

定义几个变量P[i]表示在处理过后的数组中的第i个点最长向左边拓展P[i]个数位都能保证他是回文(右边不记录因为左右对称)

那么p[1]=1,p[2]=2,p[3]=1,p[4]=2,p[5]=1,p[6]=2,p[7]=5,p[8]=2,p[9]=1,p[10]=2,p[11]=1,p[12]=2,p[13]=1;

可以发现所有#的地方大部分都是1

其实上面那句话是错的!!!  p[7]=5 ???

重点来了:

P[i]-1代表什么? 原串(不插入#前)中第i位置的回文串长度(包含中间点)

证明:

1、显然L=2*P[i]-1即为新串(加#)中以第i个点为中心最长回文串长度。
2、以第i位 为中心的回文串一定是以#开头和#结尾的,例如“#b#b#”或“#b#a#b#”
所以L减去最前或者最后的‘#’字符就是原串中长度的二倍,即原串长度为(L-1)/2,化简的P[i]-1。

得证。

【再偷2张图片】

maxId表示前面运行过程中求得最大回文串的最右端

id(后面程序是MID)表示此时最大回文串的对称点,i代表当前遍历到第i个位置

显然我们可以轻易算出i关于id(MID)对称点的坐标j

由id是ij的中点可知 (i+j)/2=id,由此可知 j=2*id-i(所以程序运行的时候就不用记录j了直接用id和i算就行)

运用动态规划的思想在算P[i]的时候P[j] | j∈ [1,i) 都已经算出那么若j为i关于id(MID)的对称点那么P[i]∈ [P[j],+∞)

就是P[i]不可能比P[j]短,为什么呢?

利用回文串的对称性j和i关于id(MID)对称,MaxId和左边MinId(对于id(MID)的对称点)关于id对称

那么j到id回文串半径为P[j],对称过来到id右边i的左边那么回文串半径也为P[j],

而P[i]没有算出来所以就是P[i]不可能比P[j]短 即 P[i]∈ [P[j],+∞)

分类讨论:

若对称过来超过MaxId那么这样的对称是不合法的 P[j]=1从该点老老实实向两边拓展

就是P[j]对称到i的左右,i右端超过MaxId(触及不该触及的地方),就是不合法,因为右边你并不知道

若对称过来没有超过MaxId那么这样的对称是合法的 P[i]=p[j]然后往右拓展

在移动的过程中顺便更新MaxId和P[i]就行

到这里你已经完成了 Subtask3 对于100%的数据|S|<10000000的数据

复杂度证明?

manacher算法只需要线性扫描一遍预处理后的字符串。
对p[]数组的处理 i 为 j 关于 id 的对称点

1. (i<MaxId)

  • Maxid-i>p[j] p[j]=p[i]
  • 其他情况 p[i]=MaxId-i

2.其他情况 p[i]=0

1. 在i<MaxId的情况下,p的值可以在O(1) 时间内确定
2. 在i>MaxId 的情况下,p的值需要O(n) 的时间内确定,

但是在情况2下,每次扫描都从MaxId开始,且MaxId自身的变化情况是单调递增的

这样可以保证,字符串中的每个字符最多被访问2次

所以,该算法的时间复杂度是线性O(n)

只需要弄清楚两点:

1.while()循环本身的时间复杂度在没有前提条件的情况下确实是O(n)

2.但是这里的MaxId,是不断往后走而不可能往前退的,它自身的值的变化是递增的。

那么你可以明白,要进入while循环,

i 的值必然是比MaxId大的,

也就是说整个程序结束为止,

while循环执行的操作数为n次(线性次),

而字符串中的每个字符,最多能被访问到2次。

时间复杂度必然为O(n)

贴下代码:

# include <bits/stdc++.h>
using namespace std;
const int MAXN=11000005*2;
char a[2*MAXN];
int p[2*MAXN];
int main()
{
    char ch=getchar();int t=0; a[0]=‘?‘;//为了保险起见a[0]和最后的符号不能一样
    while (isalpha(ch)) {
        t++;
        a[2*t]=ch; a[2*t-1]=‘#‘;
        ch=getchar();
    }
    a[2*t+1]=‘#‘;
    int n=2*t+1;
    int MID=0,R=0,i; //MID就是id,R就是MaxId,i就是i
    for (i=1;i<=n;i++) {
        if (R>i) p[i]=min(p[2*MID-i],R-i);
        else p[i]=1;
        while (a[i-p[i]]==a[i+p[i]]) p[i]++;
        if (i+p[i]>R) R=i+p[i],MID=i;
    }
    int ans=0;
    for (int i=1;i<=n;i++) ans=max(ans,p[i]-1);
    printf("%d\n",ans);
    return 0;
}

这是一道真正的模板题qwq:

P3805 【模板】manacher算法

题目描述

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.

字符串长度为n

输入输出格式

输入格式:

一行小写英文字符a,b,c...y,z组成的字符串S

输出格式:

一个整数表示答案

输入输出样例

输入样例#1:

aaa

输出样例#1:

3

说明

字符串长度|S| <= 11000000

 这里有着注意点:不能判断回车否则是TLE,应该判断不是字母时果断跳出

原文地址:https://www.cnblogs.com/ljc20020730/p/9546690.html

时间: 2024-10-16 23:56:37

浅谈字符串算法(KMP算法和Manacher算法)的相关文章

时空权衡之输入增强 ----字符串匹配算法Horspool算法和Boyer-Moore算法

在算法设计的时空权衡设计技术中,对问题的部分或者全部输入做预处理,对获得的额外信息进行存储,以加速后面问题的求解的思想,我们称作输入增强. 其中字符串匹配算法Horspool算法和Boyer-Moore算法就是输入增强的例子. 首先了解一下字符串匹配的概念.我们把在一个较长的n个字符的串中,寻找一个给定的m个字符的串的问题,称为字符串匹配问题.较长的串称为text,而需要寻找的串称为pattern. 字符串匹配问题的蛮力算法很好理解:我们把pattern与text第一个字符对齐,从左往右比较pa

单源最短路径算法——Bellman-ford算法和Dijkstra算法

 BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离:(运行|v|-1次) 3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛.如果存在未收敛的顶点,则算法返回false,表明问题无解:否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中. 1 BELLMAN-FORD

TCP_NODELAY和TCP_CORK nagle算法和cork算法

TCP_NODELAY 默认情况下,发送数据采用Nagle 算法.这样虽然提高了网络吞吐量,但是实时性却降低了,在一些交互性很强的应用程序来说是不允许的,使用TCP_NODELAY选项可以禁止Nagle 算法. 此时,应用程序向内核递交的每个数据包都会立即发送出去.需要注意的是,虽然禁止了Nagle 算法,但网络的传输仍然受到TCP确认延迟机制的影响. TCP_CORK 所谓的CORK就是塞子的意思,形象地理解就是用CORK将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去.设置该选项后,内

浅谈MySQL索引背后的数据结构及算法

摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持 也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等.为了避免混乱,本文将只关注于BTree索引,因为这是 平常使用MySQL时主要打交道的索引,至于哈希索引和全文索引本文暂不讨论. 文章主要内容分为四个部分. 第一部分主要从数据结构及算法理论层面讨论MySQL数据库索引的数理基础. 第二部分结合MySQL数据库中

MP算法和OMP算法及其思想

主要介绍MP(Matching Pursuits)算法和OMP(Orthogonal Matching Pursuit)算法[1],这两个算法尽管在90年代初就提出来了,但作为经典的算法,国内文献(可能有我没有搜索到)都仅描写叙述了算法步骤和简单的应用,并未对其进行详尽的分析,国外的文献还是分析的非常透彻,所以我结合自己的理解,来分析一下写到博客里,算作笔记. 1. 信号的稀疏表示(sparse representation of signals) 给定一个过完备字典矩阵,当中它的每列表示一种原

使用Apriori算法和FP-growth算法进行关联分析(Python版)

===================================================================== <机器学习实战>系列博客是博主阅读<机器学习实战>这本书的笔记也包含一些其他python实现的机器学习算法 算法实现均采用python github 源码同步:https://github.com/Thinkgamer/Machine-Learning-With-Python ==================================

最短路径Dijkstra算法和Floyd算法整理、

转载自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最短路径—Dijkstra算法和Floyd算法 Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹

Dijkstra算法和Floyed算法

写的比较好的三篇文章 Floyed算法 最短路径-Dijkstra算法和Floyed算法 最短路径之Dijkstra算法和Floyed算法 哈哈,他山之石,可以攻玉 自己有心得,慢慢补充

链接挖掘算法之PageRank算法和HITS算法

参考资料:http://blog.csdn.net/hguisu/article/details/7996185 更多数据挖掘算法:https://github.com/linyiqun/DataMiningAlgorithm 链接分析 在链接分析中有2个经典的算法,1个是PageRank算法,还有1个是HITS算法,说白了,都是做链接分析的.具体是怎么做呢,继续往下看. PageRank算法 要说到PageRank算法的作用,得先从搜索引擎开始讲起,PageRank算法的由来正式与此相关. 搜