• 回文树介绍


    回文树例题看这 : 点击

    简介


      我们知道,Manacher算法可以在[Math Processing Error]O(n)的时间内求出以每个位置为中心的最长回文串(虽然我昨天还不知道Manacher算法是怎么做的)。但是如果要统计回文串的个数,Manacher就捉襟见肘了。于是,回文自动机闪亮登场!
      回文自动机是解决回文串问题的一类数据结构。
      这个数据结构比较新,是由来自战斗民族的MikhailRubinchik在2014年的Petrozavodsk夏令营提出。(http://codeforces.com/blog/entry/13959)
      回文树其实是两棵树,分别是偶数长度的回文树和奇数长度的回文树,树中每一个节点代表一个回文串。
      而回文自动机的方法与Manacher迥乎不同,反倒与KMP和AC自动机类似,所以如果你不会Manacher,也不必像我一样去学。

    前置技能


      KMP、AC自动机。

    数据说明


    len[i]:节点i的回文串的长度
    next[i][c]:节点i的回文串在两边添加字符c以后变成的回文串的编号(和字典树的next指针类似)
    fail[i]:类似于AC自动机的fail指针,指向失配后需要跳转到的节点(即为i的最长回文后缀且不为i)
    cnt[i]:节点i表示的回文串在S中出现的次数(建树时求出的不是完全的,count()加上子节点以后才是正确的)
    num[i]:以节点i回文串的末尾字符结尾的但不包含本条路径上的回文串的数目。(也就是fail指针路径的深度)
    last:指向以字符串中上一个位置结尾的回文串
    cur: 指向由next构成的树中当前回文串的父亲节点(即当前回文串是cur左右两边各拓展一个字符得来)
    S[i]:第i次添加的字符
    p:添加的节点个数
    n:添加的字符个数

    分析


      假设现在我们有串S=’abbaabba’。
      先建两个树根,偶数长度的根为0,奇数长度的根为1。注意,我们将len[0]设为0,但将len[1]设为-1。将p、n、last均初始化为0。将S[0]设为-1,这是放一个字符集中没有的字符,减少特判。然后,我们将fail[0]指向1。
      举个例子,若有串S=’abbaabba’。
      首先我们添加第一个字符’a’,S[++ n] = ‘a’,然后将cur赋为get_fail(last)。其中的get_fail函数就是让找到第一个使得S[n-len[last]-1]==S[n]的last。注意,此处的n不为get_fail中的参数,依然为添加的字符个数。这样做的话,我们就可以通过fail构成的失配链找到last的所有回文后缀(包括它自己),然后从长到短依次判断此后缀的前一位是否等于S[n],等于则表明可以构成一个回文串。
      判断此时next[cur][‘a’]是否已经有后继,如果next[cur][‘a’]没有后继,我们就进行如下的步骤:
      新建节点(节点数p++,且之后p=3),并让now等于新节点的编号(now=2),则len[now]=len[cur]+2(每一个回文串的长度总是在其最长子回文串的基础上在两边加上两个相同的字符构成的,所以是+2,同时体现出我们让len[1]=-1的优势,一个字符自成一个奇回文串时回文串的长度为(-1)+2=1)。
      然后我们让fail[now]=next[get_fail ( fail[cur] )][‘a’],即得到fail[now](此时为fail[2] = 0)。计算get_fail(fail[cur])是为了求出在cur的所有回文后缀中(不包括它自己,因为和AC自动机一样,fail[now]不能指向now),满足前一位等于S[n]的后缀,我们即可用它来往两边拓展一格,即为now的最长回文后缀(不包括它自己)。然后next[cur][‘a’] = now。
      当上面步骤完成后我们让last = next[cur][‘a’](不管next[cur][‘a’]是否有后继),然后cnt[last] ++。
      如上述方法插完所有字符后,我们将节点x在fail指针树中将自己的cnt累加给父亲,从叶子开始倒着加,最后就能得到串S中出现的每一个本质不同(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同可以使用burnside引理)回文串的个数。
      构造回文树需要的空间复杂度为[Math Processing Error]O(N∗字符集大小),时间复杂度为O[Math Processing Error](N∗log(字符集大小)),这个时间复杂度比较神奇。如果空间需求太大,可以改成邻接表的形式存储,不过相应的要牺牲一些时间。

    功能

    求串S前缀0~i内本质不同回文串的个数
    求串S内每一个本质不同回文串出现的次数
    求串S内回文串的个数(其实就是1和2结合起来)
    求以下标i结尾的回文串的个数

  • 相关阅读:
    Visual Studio各版本区别
    调试ASP.NET网站IIS环境问题解决方法汇总
    关于Webpage Not Found问题解决~~~
    SpringCloud+vue搭建的商城项目
    springcloud实现限流
    ReentrantLock
    栈和堆的特点
    你用对锁了吗?浅谈 Java “锁” 事
    ReentrantReadWriteLock读写锁的使用
    OAuth2
  • 原文地址:https://www.cnblogs.com/DWVictor/p/11324257.html
Copyright © 2020-2023  润新知