zoukankan      html  css  js  c++  java
  • 回文树 回文自动机 PAM 入门学习详解

    回文树 回文自动机 PAM

    是什么

    • 顾名思义,回文自动机是一种自动机,其中的每个状态表示的是一个回文串,可以用来解决与回文串有关的许多问题。
    • 和其他自动机一样,它也有转移边(树边)和失配指针(fail)。其中转移边表示的是在原回文串的两边各加一个字符,得到长度加 2 2 2的新回文串;fail指针则指向该回文串的最长回文后缀。
    • 和其他自动机有所不同,它有两个根节点, 分别代表长度为偶数的串和长度为奇数的串。它们的长度分别为 0 0 0 − 1 -1 1(注意不是 1 1 1,为了添加 2 2 2的长度可以得到长为 1 1 1的回文串),以下分别称为奇根偶根
    • 值得注意的是,偶根的fail指针指向的是奇根,而奇根的fail并不需在意,它的儿子中总会有长为 1 1 1的回文串,因而不可能会失配。
    • 它大概是这样:

    在这里插入图片描述

    如何构造

    • 首先初始化两个根节点。
    	a[0].len = 0, a[1].len = -1;
    	a[0].fail = 1;
    	la = 0;//上一个字符的最长回文后缀
    
    • 已经构造好了前 i − 1 i-1 i1个字符的回文树,当前需要加入第 i i i个字符 c c c
    • 从上一个字符的最长回文后缀开始,不断往fail链走,直到能够满足 c = s t i − l e n − 1 c=st_{i-len-1} c=stilen1,即直到某个串的左边的字符和它右边的字符 c c c相同。
    • 那么这个点的 c c c儿子即为当前的最长回文后缀,注意需要判断 c c c儿子是否存在,如果不存在需要添加。
    	int x = la;
    	while(st[i] != st[i - 1 - a[x].len]) x = a[x].fail;
    	if(!a[x].p[st[i] - 'a']) a[x].p[st[i] - 'a'] = ++tot;//如果没有儿子则添加
    	la = a[x].p[st[i] - 'a'];
    	a[la].len = a[x].len + 2;
    
    • 如果新建了一个点,则需要找到这个点的fail指针指向哪里。
    • 类似AC自动机地,从它的父亲的fail开始不断往fail链跳,但是不能像AC自动机一样判断当前点是否有 c c c这个儿子,而是判断当前回文串的前一个字符是否为 c c c
    • 同时注意,当新建的点是长为 1 1 1的回文串时,fail指针要赋为偶根。(这个点的父亲是奇根,而此时我们不需要往它父亲的fail指针走,这就是为什么开始时提到不需要在意奇根的fail)
    	if(a[la].len == 1) x = 0;
    	else {
    		x = a[x].fail;
    		while(st[i] != st[i - 1 - a[x].len]) x = a[x].fail;
    		//错误写法:while(!a[x].p[st[i] - 'a']) x = a[x].fail;
    		x = a[x].p[st[i] - 'a'];
    	}
    	a[la].dp = a[x].dp + 1;
    	a[la].fail = x;
    	ls = a[la].dp;
    
    • 过程示意图如下:
      在这里插入图片描述

    一些思考

    思考一
    • 前面提到的“不能”,是为什么呢?
    • 在AC自动机上,fail指针指向能匹配上的最长后缀,在父亲跳fail时,只要跳到的点能有 c c c儿子,则与当前串必然相同;
    • 而在回文树上,fail指针指向的必须是当前回文串的最长回文后缀,尽管跳到的点有 c c c儿子,但不能保证关于中心对称的另一边一定有字符 c c c,所以不一定满足。
    • 简单举个例子:
    • AC自动机中,求abbba串的fail指针,从abbb开始跳,若跳到bb时有a儿子,则必然会有bba串,于是可以把fail指针连给它;
    • 回文树中,求abbba串的fail指针,从bbb开始跳,若跳到bb时有a儿子,但发现abbba串中并没有abba这样的回文串,所以单单这样判断是不行的。
    思考二
    • 正确的求法中,并没有判断是否有存在这样的一个儿子,那万一它不存在呢?
    • 由于这是一个回文串,把它的fail指针位置关于中心对称,必然存在一个相同的串,所以它必然已经出现在回文树中了。

    模板题

    P5496 【模板】回文自动机(PAM)
    P3649 [APIO2014]回文串

  • 相关阅读:
    SpringMVC之使用ResponseEntity
    紧随时代的步伐--Java8特性之接口默认方法
    Executor多线程框架
    Jsoup入门
    Echart、Excel、highcharts、jfreechart对比
    JFreeChart入门
    Spring定时任务(@Scheduled)
    Java正则表达式入门基础篇
    Vue.js之入门
    springboot rabbitmq direct exchange和topic exchange 写法上关于路由键的区别
  • 原文地址:https://www.cnblogs.com/LZA119/p/14279489.html
Copyright © 2011-2022 走看看