现在讲的也是一种处理字符串的方法,叫做Manacher,有点像“马拉车”

1179: [视频]【Manacher】最长回文子串

时间限制: 1 Sec  内存限制: 128 MB
提交: 209  解决: 120
[提交] [状态] [讨论版] [命题人:admin]

题目描述

【题意】
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文子串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
【输入格式】
输入有多组数据,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
每组数据之间由空行隔开(该空行不用处理)
字符串长度len<=100000
【输出格式】
每一行一个整数x,对应一组数据,表示该组数据的字符串中所包含的最长回文子串长度.
【样例输入】
aaaa

abab
【样例输出】
4 3

朴素做法:3重for循环:第一重枚举开头,第二重枚举结尾,第三重用来判断是不是回文串,记录答案更新最大值

但是我们的数据范围是10万,三重for循环是不可能不炸的

然后就要用到我们的Manacher,但是这道题也可以用后缀数组来做,不过Manacher更简单方便,下面我们来看一下“马拉车” 

                  a w e r s e s s a

              # a # w # c # r # s # e # s # s # a #

我们看到一个串,要判断他的串时,我们就要分他的长度是单数还是复数的情况,为什么呢?因为我们的回文是有两种情况的,单数和偶数的情况是不一样的
(非常不方便)
所以我们在头和尾,以及两个字符之间加一个#号,(其他的也可以,只要是字符串当中没有出现过的就可以)
那么经过这个操作之后,不过原来是偶数还是奇数,加上#号之后都会变成奇数,这样我们处理起来就会方便很多

            # a # w # c # r # s # e # s # s # a #
         p[ ]=  1 2 1 2 1 2 1 2 1 2 1 4 1 2 3 2 1 2 1
首先#号是自己一个,所以是1
a是与# a #构成,所以是2,剩下的都是同样的道理
一直到e的出现,他是 # s # e # s # 所以是4,这样我们就知道了p数组的实现了

p[i]表示以i为中心构成的最长回文字串的结尾与i的距离(结果要+1,因为算上了i)

然后我们看回e,是第12个字符,p[12]=4,然后他的这个回文串的长度是7,向右延伸4个,向左延伸4个,重复算了1个,所以就是4*2-1=7(包括#号)
然后因为答案是不算#号的,所以真正的是 (7-1)/2=3
因为# s # e # s #头尾都有#号,所以删掉一个之后,就是一个#号与一个数字搭配,这样就可以直接除以2算出字串的真正长度

所以我们发现最长回文字串的长度就是
=(p[i]*2-1-1)/2
=(p[i]*2-2)/2
=p[i]-1  (一定要记住这个最后的变式,特别重要)

看了我前面博客的一定都知道,这是我目前在讲字符串专题的时候用到的图是最简单的了

然后我们看到图
首先字符串的长度是1~len
l,r表示之前计算的最长回文字串的开头和结尾,r代表结尾所到达的最大的地方,l代表开头
pos代表回文串的中心

分类讨论 (有两种情况)

1.i<r
我们可以发现p[i]=p[j]
因为l~r是一个回文串,那么我们就可以直接得出p[i]=p[j],那么p[i]就可以直接继承了

2.i>=r
这个时候就不能直接继承了,因为我们不知道i后面的p数组是怎样的,所以我们只能直接暴力求出p[i]
然后j这个位置我们可以

p[i]=min(p[j],r-i)
因为有时候i所构成的最长回文字串是比r还要长的,那么这个时候我们就只能继承i~r这个长度,不能继承到后面,因为我们根本不知道绿色格子的长度是不是相等的,所以我们不能继承到后面
有因为i与j对称,所以直接p[j]就可以了

然后j的话,我们可以用 pos-(i-pos)=pos*2-i
因为i到pos的距离等于j到pos的距离,因为i是以pos为中心和j对称,也就是j是i的对称点

所以我们可以直接用pos*2-i=j来确定j的位置

然后Manacher讲到这里的时候,我们的这道题就已经解决的差不多了,剩下的就是看代码的实现

(注释版 因为这个相对于EXKMP要好理解很多,所以可以先试着自己打一遍,再看注释深刻理解)

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
char s[],tt[];/*tt表示新字符串,s表示原字符串*/
int p[],ans;
void Manacher()
{
int len=strlen(s+);
for(int i=;i<=len;i++) tt[*i-]='#',tt[*i]=s[i];/*构造带#新字符串*/
len=len*+;/*新字符串的长度*/
tt[len]='#';/*最后面也是#号*/
int pos=,r=;
/*r为之前计算的最长回文字串的结尾所到达的最大地方(右端点的最大值)
pos表示r所在最长回文字串的中心*/
for(int i=;i<=len;i++)
{
int j=*pos-i;/*j为i的对称点*/
if(i<=r) p[i]=min(p[j],r-i);
/*
如果i<=R的话,分两种情况
第一种情况p[j]>R-i时,表示j对称点的最长回文子串已经越出R的界限了
这时,因为我们不确定大于R时的情况,所以p[i]暂时等于R-i
第二种情况p[j]<=R-i时,那就可以直接继承p[j]得p[i]=p[j]
综合以上两种情况,我们可以用p[i]=min(p[j],R-i)来归纳
*/
else p[i]=;/*不然就为1,就是自己成为一个回文串*/
while(<=i-p[i] && i+p[i]<=len && tt[i-p[i]]==tt[i+p[i]]) p[i]++;
/*i-p[i]就是i的前面几个位置>=1,而且i的右边几个位置<=len,也就是说我构成的p[i]长度的是不会不合法的,
如果i-p[i]的字符等于i+p[i]的话,也就是说我的回文是成立的,变长了,那么长度增加*/
if(i+p[i]>r)/*右端点比r大*/
{
pos=i;/*把pos定为i*/
r=i+p[i];/*更新r*/
}
}
ans=;
for(int i=;i<=len;i++) ans=max(ans,p[i]-);/*p[i]-1就是真正回文串的长度
那么有#号怎么办,有#号也没有关系,因为p[i]-1终究是没有#号的,其实这个我们直接推出来的过程,而且记录的是最大值,
所以的话,根本不会出现这种情况*/
/*
首先当前的最长回文子串长度为2*p[i]-1
因为我们得到的p数组是在加了#号后的字符串上操作的,所以我们要对答案进行处理
因为#号处于首尾和每个字符之间,所以我们就可以保证所得出的最长回文子串的首尾都为#
这时我们可以得出不带#号的回文串的长度为(2*p[i]-1-1)/2=p[i]-1
所以真正的最长回文子串就是p[i]-1
ans记录最长的回文子串长度
*/
}
int main()
{
while(scanf("%s",s+)!=EOF)
{
Manacher();
printf("%d\n",ans);
}
return ;
}

Tristan Code 注释版

 #include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
char s[],tt[];
int p[],ans;
void Manacher()
{
int len=strlen(s+);
for(int i=;i<=len;i++) tt[*i-]='#',tt[*i]=s[i];
len=len*+;
tt[len]='#';
int pos=,r=;
for(int i=;i<=len;i++)
{
int j=*pos-i;
if(i<=r) p[i]=min(p[j],r-i);
else p[i]=;
while(<=i-p[i] && i+p[i]<=len && tt[i-p[i]]==tt[i+p[i]]) p[i]++;
if(i+p[i]>r)
{
pos=i;
r=i+p[i];
}
}
ans=;
for(int i=;i<=len;i++) ans=max(ans,p[i]-);
}
int main()
{
while(scanf("%s",s+)!=EOF)
{
Manacher();
printf("%d\n",ans);
}
return ;
}

Tristan Code 非注释版

Manacher模版的更多相关文章

  1. HDU 3294 Manacher模版题

    点击打开链接 题意:求最长回文子串所在的区间,然后第一个字符代表a,以后的顺推,最后输出这个区间顺推后的串 思路:Manacher轻松水过.记录下最长回文串的位置和长度即可了,然后输出时自己处理一下, ...

  2. Ural 1297 Palindrome(Manacher或者后缀数组+RMQ-ST)

    1297. Palindrome Time limit: 1.0 second Memory limit: 64 MB The “U.S. Robots” HQ has just received a ...

  3. P3805 【模版】manacher算法(manacher)

    P3805 [模版]manacher算法 题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入输出格式 输入格式: 一行小写英文字符a ...

  4. P3805 【模版】manacher算法

    题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入输出格式 输入格式: 一行小写英文字符a,b,c...y,z组成的字符串S 输出格 ...

  5. hdu_3068_最长回文(Manacher)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题意:给你一个字符串,让你求最长的回文子串. 题解:数据量比较大,暴力O(n2)会超时,直接上马 ...

  6. Manacher详解

    之前的字符串题解中对Manacher的思想进行了简略的介绍,在这篇文章中,我将会详细的将这个算法的初衷和具体实现理论进行解释.声明一点,这是我个人的理解,可能有不全面之处,望多包涵.在之前的几篇文章中 ...

  7. Kuangbin 带你飞 KMP扩展KMP Manacher

    首先是几份模版 KMP void kmp_pre(char x[],int m,int fail[]) { int i,j; j = fail[] = -; i = ; while (i < m ...

  8. 创建ABPboilerplate模版项目

    本文是根据角落的白板报的<通过ABPboilerplate模版创建项目>一文的学习总结,感谢原文作者角落的白板报. 1 准备 开发环境: Visual Studio 2015 update ...

  9. 使用boilerplate模版创建解决方案

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 话不多说,让我们开始干吧!对于还没有接触ABP框架或者接触时间还不是很长的小伙伴来说,我建议还是使用官方建议的做法,那就是到ABP ...

随机推荐

  1. 使用Python操作Excel文档(一)

    Python | 使用Python操作Excel文档(一) 0 前言 在阅读本文之前,请确保您已满足或可能满足以下条件: 请确保您具备基本的Python编程能力. 请确保您会使用Excel. 请确保您 ...

  2. R-三次指数平滑法实践

    data <- read.csv("H://day_shuaka.csv") raw0 <- data[359:752,] raw0$weekday <- as. ...

  3. find命令不递归查询子目录

    [root@dbrg-2 test]# find .  ! -name "." -type d -prune -o -type f -name "*.jpg" ...

  4. Travis-CI自动化测试并部署至自己的CentOS服务器

    一直都想自己部署一下自动化测试部署,在了解了Travis-CI之后终于准备在这次和小伙伴一起做的一个博客类网站实验下了. 因为这是一个前后端分离的项目,所以我这里只管前端工程的自动化部署,前端主要用V ...

  5. Linux 下搭建Git 服务器详细步骤

    参考: https://www.cnblogs.com/dee0912/p/5815267.html#_label0 https://blog.csdn.net/carfge/article/deta ...

  6. Fastadmin 后台表单,外键关键,步骤

    1.在 public\assets\js\backend 目录中找到对应的js,修改 2.对应控制器中加上index()方法:开启关联查询 重点: protected $relationSearch ...

  7. What happens in an async method

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-pr ...

  8. linu逻辑分区动态调整大小

    注意: 这个动态调整的方法是有丢数据风险的,要确保调整的源分区没有使用或者使用率很低.源分区中如果有重要的文件最好先备份 在centos 6.5上操作过 lvdisplay 查看已有的分区的大小 lv ...

  9. iOS即时通讯之CocoaAsyncSocket源码解析三

    原文 前言 本文实例Github地址:即时通讯的数据粘包.断包处理实例. 本文旨以实例的方式,使用CocoaAsyncSocket这个框架进行数据封包和拆包.来解决频繁的数据发送下,导致的数据粘包.以 ...

  10. Groovy脚本基础全攻略

    1 背景 Groovy脚本基于Java且拓展了Java,所以从某种程度来说掌握Java是学习Groovy的前提,故本文适用于不熟悉Groovy却想快速得到Groovy核心基础干货的Java开发者(注意 ...