博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Manacher算法 - 求最长回文串的利器
阅读量:7122 次
发布时间:2019-06-28

本文共 2574 字,大约阅读时间需要 8 分钟。

 

求最长回文串的利器 - Manacher算法

Manacher主要是用来求某个字符串的最长回文子串.

不要被manacher这个名字吓倒了,其实manacher算法很简单,也很容易理解,程序短,时间复杂度为O(n).

求最长回文子串这个问题,我听说有个分治+拓展kmp的算法,后缀数组也可以.

但是对于求回文串来说,manacher算法肯定有很多其他算法没有的优点。

现在进入正题:

首先,在字符串s中,用rad[i]表示第i个字符的回文半径,即rad[i]尽可能大,且满足:

s[i-rad[i],i-1]=s[i+1,i+rad[i]]

很明显,求出了所有的rad,就求出了所有的长度为奇数的回文子串.

至于偶数的怎么求,最后再讲.

假设现在求出了rad[1..i-1],现在要求后面的rad值,并且通过前面的操作,得知了当前字符i的rad值至少为j.

现在通过试图扩大j来扫描,求出了rad[i].再假设现在有个指针k,从1循环到rad[i],试图通过某些手段来求出[i+1,i+rad[i]]的rad值.

根据定义,黑色的部分是一个回文子串,两段红色的区间全等.

因为之前已经求出了rad[i-k],所以直接用它.有3种情况:

①rad[i]-k<rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分超过了黑色的部分,所以rad[i+k]肯定至少为rad[i]-k,即橙色的部分.

那橙色以外的部分就不是了吗?这是肯定的.因为如果橙色以外的部分也是回文的,那么根据青色和红色部分的关系,可以证明黑色部分再往外延伸一点也是一个回文子串,这肯定不可能,因此rad[i+k]=rad[i]-k.为了方便下文,这里的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).

②rad[i]-k>rad[i-k]
如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分在黑色的部分里面,根据定义,很容易得出:rad[i+k]=rad[i-k].为了方便下文,这里的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).
根据上面两种情况,可以得出结论:当rad[i]-k!=rad[i-k]的时候,rad[i+k]=min(rad[i]-k,rad[i-k]).
注意:当rad[i]-k==rad[i-k]的时候,就不同了,这是第三种情况:
如图,通过和第一种情况对比之后会发现,因为青色的部分没有超出黑色的部分,所以即使橙色的部分全等,也无法像第一种情况一样引出矛盾,因此橙色的部分是有可能全等的,但是,根据已知的信息,我们不知道橙色的部分是多长,因此就把i指针移到i+k的位置,j=rad[i-k](因为它的rad值至少为rad[i-k]),等下次循环的时候再做了.
整个算法就这样.
至于时间复杂度为什么是O(n),我已经证明了,但很难说清楚.所以自己体会吧.
上文还留有一个问题,就是这样只能算出奇数长度的回文子串,偶数的就不行.怎么办呢?有一种直接但比较笨的方法,就是做两遍(因为两个程序是差不多的,只是rad值的意义和一些下标变了而已).但是写两个差不多的程序是很痛苦的,而且容易错.所以一种比较好的方法就是在原来的串中每两个字符之间加入一个特殊字符,再做.如:aabbaca,把它变成a#a#b#b#a#c#a,这样的话,无论原来的回文子串长度是偶数还是奇数,现在都变成奇数了.

模板题:HDU 3068

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-08-19-15.59
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define maxx 20000050
#define  LL long long
#define  ULL unsigned long long
using
namespace
std;
char
str
[
2
*
maxx
];
char s
[
maxx
];
int p
[
maxx
];
void
Manacher(
int
*p
,
char
*
str
,
int
len)
{
     
int
mx
=
0;
     
int
idx
=
0;
     
for(
int
i
=
1;
i
<
len;
i
++)
     
{
           p
[
i
]
=
mx
>
i
?
min(p
[
2
*
idx
-
i
],
mx
-
i)
:
1;
           
while(
str
[
i
+p
[
i
]]
==
str
[
i
-p
[
i
]])
                 p
[
i
]
++;
           
if(
i
+p
[
i
]
>
mx)
           
{
                 
mx
=
i
+p
[
i
];
                 
idx
=
i;
           
}
     
}
}
int
main()
{
     
while(
scanf(
"%s"
,s)
!=
EOF)
     
{
           
int
len
=
strlen(s);
           
int n
=
2
*
len
+
2;
           
str
[
0
]
=
'$';
           
for(
int
i
=
0;
i
<=
len;
i
++)
           
{
                 
str
[
2
*
i
+
1
]
=
'#';
                 
str
[
2
*
i
+
2
]
=s
[
i
];
           
}
           
Manacher(p
,
str
,n);
           
int
ans
=
1;
           
for(
int
i
=
0;
i
<n;
i
++)
                 
ans
=
max(
ans
,p
[
i
]);
           
printf(
"%d
\n
"
,
ans
-
1);
     
}
     
return
0;
}

 

转载地址:http://zxxel.baihongyu.com/

你可能感兴趣的文章
滴滴开源支撑业务代码重构工具Rdebug
查看>>
联合国儿童基金会投资六家区块链初创企业,目标是解决“全球性挑战”
查看>>
CNCF宣布Envoy项目正式毕业
查看>>
百度App网络深度优化系列(一):DNS优化
查看>>
Oracle发布多语种虚拟机平台GraalVM 1.0
查看>>
GCM 3.0采用类似方式向Android、iOS和Chrome发送消息
查看>>
如何进行5万并发用户负载测试?
查看>>
Java日志性能那些事
查看>>
埃森哲、亚马逊和万事达卡抱团推出的区块链项目有何神通?
查看>>
书评 - 《展望敏捷软件测试》
查看>>
Linux之父为过去的言行道歉,宣布离开社区反思
查看>>
使用Visual Studio Code进行Swift开发
查看>>
AWS Amplify Console:赋予应用程序快速部署的能力
查看>>
C#的未来:不可变变量
查看>>
Uber推出数据湖集成神器DBEvents,支持MySQL、Cassandra等
查看>>
InfoQ趋势报告:架构和设计领域技术演变详解
查看>>
揭秘码云:全球第二大代码托管平台的核心架构
查看>>
距离QCon纽约还有3个礼拜:新的演讲、播客节目和研讨会
查看>>
Spark Streaming中流式计算的困境与解决之道
查看>>
15条软件开发黄金定律
查看>>