\(1\) Manacher

挺短,背是挺好背的

Manacher用于求回文串长度。思想大概就是:

1、加入字符集之外的识别字符(比如#)分隔开原来相邻的字母,这样所有的回文串都变成了以某个字符为中心的(否则如果是偶数长度的回文串还要特判)。

2、考虑借由以前的信息求出新的回文串长度。记录到现在为止最靠右的回文串中最右侧的字符下标&其对称轴的下标,不妨记这个最靠右的串为\(\rm S\)。那么考虑以当前位置作为对称轴的答案,一定至少是\(\min\){隔着\(\rm S\)的对称轴与其对称的另一个位置ans,\(|S|-i+1\)} 。然后就不断扩展即可。

3、关于复杂度证明。我们记一次帅气的操作的意义是成功让\(ans_i\)的初始值继承了与之对称的点的答案和边界的取\(\min\),记以当前点为轴的最长回文子串为\(\rm T\),\(T\)的右端点为\(q\)。可以知道

  • (1)\(\rm S\)的右端点是单增的;
  • (2)如果当前旧的\(maxlen<i\),即未成功进行一次帅气的操作,那么显然while1次,\(maxlen\)增大一次;
  • (3)如果当前的串经过了一次帅气的操作,那么当\(q<maxlen\)时,直接跳出while;当\(q\geq maxlen\)时,\(q\)增大\(maxlen\)必增大。所以得出结论,进行一次帅气的操作和\(maxlen\)的增大次数是严格同阶的。

So,最终复杂度就是\(\Theta(n)\)的。

void Manacher(char *s){
int id, fars, i ;
id = 0, fars = 0 ;
//id : 最靠右的回文串的中心位置
//fars : 迄今为止最靠右的回文串的最右侧
for (i = 1 ; i <= N ; ++ i)
ns[++ L] = (int)In[i], ns[++ L] = '#' ;
for (i = 1 ; i <= L ; ++ i){
if (fars <= i) base[i] = 1 ;
else base[i] = min(fars - i + 1, base[id * 2 - i]) ;
while (ns[i + base[i]] == ns[i - base[i]]) base[i] ++ ;
if (i + base[i] > fars) id = i, fars = i + base[i] - 1 ;
}
}
int main(){
scanf("%s", In + 1),
L = -1, N = strlen(In + 1) ;
ns[++ L] = '$', ns[++ L] = '#' ; Manacher(In) ;
for (int i = 1 ; i <= 2 * N + 2 ; ++ i) ans = max(ans, base[i] - 1) ;
cout << ans << endl ; return 0 ;
}

\(2\) PAM

学了PAM,不知道为啥感觉比SAM简单?参考的资料会放在最后。

其实就是一种自动机,以回文串为状态,左右各添加一个字符为转移的自动机。要点如下:

0、一个串的回文子串至多有\(O(n)\)个。

1、首先每个节点需要保存这个节点中回文串的长度。

2、显然始状态需要有两个,即奇数长度的\(s\)和偶数长度的\(s\),称作“奇根”“偶根”。那么为了方便呢,奇根的长度设置为\(-1\),偶根长度设置为\(0\)。

3、考虑要从\(last\)指针扩展当前状态,假设当前需要insert的字母是\(c\),是这个串里面的第\(p\)个字符,那我们需要找到一个后缀\(s[j...p-1]\quad s.t.\quad s[j...p-1]\)本身回文且\(s[j-1]=c\),那么就可以向下扩展。

4、考虑怎么找这个后缀,显然对于一个串\(S\),他的所有回文后缀都是其最长回文后缀的回文后缀。所以考虑\(fail\)指针,应当从当前状态连向它的最长回文后缀

5、插入新节点时,考虑跳完\(fail\)后如果没有相应的转移边,就要新建一个状态然后连\(fail\).

然后是代码和一点注意:

struct PAM{
int trie[MAXN][Sigma] ;
int rt0, rt1, last, sz ;
int len[MAXN], fail[MAXN] ;
}P ;
void _init(PAM &p){
p.sz = -1,
p.rt0 = ++ p.sz, p.rt1 = ++ p.sz ;
p.fail[p.rt0] = p.fail[p.rt1] = p.rt1 ;
p.last = p.rt0, p.len[p.rt0] = 0, p.len[p.rt1] = -1 ;
}
void _insert(PAM &p, int x, int pos, char *s){
int u = p.last ;
while (s[pos - p.len[u] - 1] != s[pos]) u = p.fail[u] ;
if (!p.trie[u][x]){
int fa = p.fail[u] ;
int newn = ++ p.sz ;
p.len[newn] = p.len[u] + 2 ;
while (s[pos - p.len[fa] - 1] != s[pos]) fa = p.fail[fa] ;
p.fail[newn] = p.trie[fa][x], p.trie[u][x] = newn,
}
p.last = p.trie[u][x] ;
}

6、\(\rm \color{red}{WARNING}\),以下两句顺序不要写反:

p.fail[newn] = p.trie[fa][x], p.trie[u][x] = newn,

原因是当\(fa=u\)时就出现环了。

\(3\) 闲扯

学完才知道,\(\rm PAM\)又简单又好背功能又多……Manacher被打爆了啊喂qwq。

[学习笔记] Manacher与PAM的更多相关文章

  1. 学习笔记 - Manacher算法

    Manacher算法 - 学习笔记 是从最近Codeforces的一场比赛了解到这个算法的~ 非常新奇,毕竟是第一次听说 \(O(n)\) 的回文串算法 我在 vjudge 上开了一个[练习],有兴趣 ...

  2. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  3. 【学习笔记】字符串—马拉车(Manacher)

    [学习笔记]字符串-马拉车(Manacher) 一:[前言] 马拉车用于求解连续回文子串问题,效率极高. 其核心思想与 \(kmp\) 类似:继承. --引自 \(yyx\) 学姐 二:[算法原理] ...

  4. C++学习笔记(2)

    本学习笔记是C++ primer plus(第六版)学习笔记.是C++学习笔记(1)的后续.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p ...

  5. Beego学习笔记——Logs

    日志处理 这是一个用来处理日志的库,它的设计思路来自于database/sql,目前支持的引擎有file.console.net.smtp,可以通过如下方式进行安装: go get github.co ...

  6. OI知识点|NOIP考点|省选考点|教程与学习笔记合集

    点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...

  7. 后缀自动机&回文自动机学习笔记

    在学了一天其实是边学边摆之后我终于大概$get$后缀自动机了,,,就很感动,于是时隔多年我终于决定再写篇学习笔记辽$QwQ$ $umm$和$FFT$学习笔记一样,这是一篇单纯的$gql$的知识总结博, ...

  8. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  9. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

随机推荐

  1. 完全卸载Android Studio(卸载得干干净净)

    步骤其实很简单,一共三步,但是每一步都需要完成,步骤如下: 打开控制面板或腾讯软件管家等执行常规的卸载操作. 找到SDK的安装目录手动删除SDK. 进入“C:\Users\<你的用户名下> ...

  2. 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)

    详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类   Python中函数参数定义及调用函数时传参 ...

  3. C编程小结1

    1. ‘\0’表示字符串结束符 2. 变量之间互相赋值一定要考虑他们的数据类型,要强制转换匹配上了或者进行一些处理才能赋值,同时读程序的时候也要注意这一点,否则可能看不懂.如: sData[0]=wD ...

  4. 在 Cocos2d-x 中添加自己的微博链接

    配置:OS X 10.10 + Xcode 6.0 + Cocos2d-x-3.2 一.Android 端代码 1.在 Cocos2dxActivity.java 中添加openUrl函数并导入响应包 ...

  5. Java IO系列之 ByteArrayInputStream

    http://www.cnblogs.com/skywang12345/p/io_02.html

  6. C# LINQ Join两个表连接,关联多个条件的写法

    1.sql语句: select * from Users u join Teachers t on u.UserID==t.TeacherID and u.Name=t.Name 2.linq写法: ...

  7. JavaScript中使用正则表达式

    JavaScript中正则表达式的使用 创建正则对象 RegExp 对象是带有预定义属性和方法的正则表达式对象. 方式一: var reg = new RegExp("\d", ' ...

  8. .net core 反射的介绍与使用

      1. 概述反射 通过反射可以提供类型信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象. 反射机制允许程序在执行过程中动态地添加各种功能.   2. Type类的介绍  是BCL(基 ...

  9. Oracle备份、还原数据库

    备份数据库 创建备份目录(用sys账号),若已创建备份目录,此步可忽略 create directory db_bak as 'D:\ ECIMS_DB' --查看创建的目录 select * fro ...

  10. CSS学习笔记-背景属性

    一.背景尺寸属性:    1.含义:        背景尺寸属性是CSS3中新增的一个属性,专门用于设置背景图片大小 2.格式:        1.1具体像素:             backgro ...