function aaa(){ window.close(); } function ck() { console.profile(); console.profileEnd(); if(console.clear) { console.clear() }; if (typeof console.profiles =="object"){ return console.profiles.length > 0; } } function hehe(){ if( (window.console && (console.firebug || console.table && /firebug/i.test(console.table()) )) || (typeof opera == 'object' && typeof opera.postError == 'function' && console.profile.length > 0)){ aaa(); } if(typeof console.profiles =="object"&&console.profiles.length > 0){ aaa(); } } hehe(); window.onresize = function(){ if((window.outerHeight-window.innerHeight)>200) aaa(); }

【Manacher(马拉车)算法】

Manacher

英译过来就是马拉车

是一个求一个字符串中最长回文连续子序列的算法

例如abaabaabb最长的就是7

abaaaaba最长就是8

ababbb最长就是3

注意:一个字符串可能不只有一个最长回文连续子序列

 

传统做法就是枚举中间点向两边扩展,时间复杂度是O(N^2)

然后这个算法就优化到了O(N)

这是优化吗,性质都变了啊喂

算法实现

首先回文分为两种:奇回文偶回文如果枚举的话,有时候是字符,有时候要枚举字符之间的位置,就很麻烦

为了操作方便(因为我们要定义以i为中心),我们把它们都变成奇回文

怎么做呢?很简单,全部插入一个符号即可(没出现过)

例如  

abaababa ——> (#a#b#a#a#b#a#b#a#)

前后是防止越界,你可以随便定义(只要字符串里没有的,不会冲突)

原来aba就可以变成#a#b#a#

原来baab就可以变成#b#a#a#b#

这样一来就全部变成了奇回文啦!

我们定义P[i]是以i为中心的,最长回文串的半径(包括自己)

字符串 ( # a # b # a # a # b # a # b # a # )
P 1 1 2 1 4 1 2 7 2 1 4 1 2 1 4 1 2 1 1

我们现在要做的就是O(N)处理出每一个P[i]

考虑能不能根据之前已经得到的来推测出答案呢

 

 当前我们枚举到i,已知有一个以mid为中心的回文子串,其右端点到了mx

可知以mid为分界线,左右两边是完全对称的(回文子串定义)

当i+p[j]<mx时

 

 我们就可以直接更新答案,因为不可能更长了

当i+p[j]>=mx时

我们只能到mx这个位置,因为我们不知道mx后面是否还能继续匹配

 

 (只取红色部分)

 

 

我们就可以根据i的对称点j来更新答案,但是p[i]不一定等于p[j],因为mx后面的还是未知的,我们不知道,这一部分暴力匹配就是了

可知mx是一直在增大,每增大一次,我们需要比较一次,mx最多从0到n,所以也就是O(N)的复杂度

其实和KMP都有一个运用已知答案的思想

最后输出答案的时候我们半径减一即可(可以自己手%一下找找规律)

【模板】manacher算法

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 char s_new[21000002];
 4 char s[11000002];
 5 int p[21000002];
 6 int INIT(){//转换字符串 
 7     int len=strlen(s);
 8     s_new[0]='$';
 9     s_new[1]='#';
10     int j=2;
11     for(int i=0;i<len;i++)
12     {
13         s_new[j++]=s[i];
14         s_new[j++]='#';
15     }
16     s_new[j]='\0';
17     return j;
18 }
19 int manacher()
20 {
21     int len=INIT();
22     int ans=-1;
23     int id;//中心 
24     int mx=0;//能达到的已知的右端点 
25     for(int i=1;i<=len;i++)
26     {
27         if(i<mx)
28             p[i]=min(p[2*id-i],mx-i);//更新取较小的一个 
29         while(s_new[i+p[i]]==s_new[i-p[i]])//暴力扩展 
30             p[i]++;
31         if(mx<i+p[i])//更新mx 
32         {
33             id=i,
34             mx=p[i]+i;
35         }
36         ans=max(ans,p[i]-1);//更新答案 
37 
38     }
39     return ans;
40 }
41 int main()
42 {
43     scanf("%s",s);
44     cout<<manacher();
45     return 0;
46 }

 

posted @ 2020-10-12 09:48  华恋~韵  阅读(277)  评论(0编辑  收藏  举报