后缀自动机的一点理解

每个点代表的都是字符串里的一个子串

可以理解为对于字符串每个字串建的\(trie\)

\(fail/link\)指向\(parent\)这个状态的最长后缀状态

\(\therefore parent.longest+1=now.shorest\)

\(endpos\):一个子串出现在原串的位置(末端点)集合

\(endpos\)集合相同的点在一个点表示串

\(parents\)树,沿着树往下就是\(endpos\)

经常我们会按\(parents\)树递归建线段树维护\(endpos\)来处理一些问题

状态

endpos相同的子串为一个点,互为后缀,长度连续且不同

建自动机

就说说最难理解的部分吧,分点转移

\(np\)是新节点,\(p\)\(lst\)某个有\(c\)的最长后缀,则\(q=trans(p,c)\)

\(q.len!=p.len+1\),也就是\(q.len>p.len+1\)
那这样\(np.fail=q\)的话,\(q.longest\)莫名其妙会成为\(np\)的后缀,但\(longest\)根本就不是从\(p\)转移过来的

所以我们单独拉出一个点\(nq\)维护\(p.longest+1\)就好了

经典匹配

在双字符串匹配中,对\(S\)\(SAM\)\(T\)在上面跑的时候,从\(i(now)\)\(i+1\)\(T\)已匹配长度为\(cnt\)

\(now\)是否有\(T[i+1]\)这个儿子

\(++cnt\)

没有:跑\(fail\)\(cnt=len[now]\)(捡长的后缀留下来),如果还没有继续跳;否则跳过去,\(++cnt\)

时间复杂度:从\(now\)\(fail\),但这次跑的花费会不会非常大,实际复杂度是有保证的,\(dep[now.fail]<dep[now]\),则相当于最多跳的次数为\(|T|\)

特殊匹配

多次查询\((l,r)\in S\)\(T\)匹配的时候怎么办呢?

题目

题目1:给出字符串\(S\),求其中每个子串出现的次数*长度的最大值
分析:区分AC自动机,AC自动机将要把每个字串提出来建\(trie\),光是提出来这个操作就已经T飞了
做法:建后缀自动机,我们知道\(parent\)树是个\(DAG\)图就可以进行\(topsort\)操作,我们按长度升序后倒序处理往\(parent\)上传

题目2:给出一个字符串\(S\),找出一系列子串\(a_1\)~\(a_k\)\(a_i\)\(a_{i+1}\)出现了至少两次
做法:首先分析后缀自动机中同一点代表的这些子串是不会相互出现两次(\(endpos\)集合相同)
字符串\(A\)出现在字符串\(B\)至少两次,所以\(A\)出现在parent树中\(B\)的祖先\(,设\)B\(的右端点位置在\)pos[B]\( 那\)[pos[B]-len[B]+len[A],pos[B]]\(里\)A\(作为末端点出现了至少两次 理解??\)pos[B]\(是右端点,\)pos[B]-len[B]\(为左端点,\)[pos[B]-len[B]+len[A]\(加上\)A\(的长度因为是\)A\(整个都在\)B\(内 而实际上作为后缀\)A\(右端点已经在\)pos[B]\(中出现过一次,所以我们只要求\)[pos[B]-len[B]+len[A],pos[B]-1]$内出现一次
而由于我们要求多次是否出现,所以线段树合并一下

题目三
\([l,r]\)\([1,l)\)中出现过则可以花\(b\)花费压缩也可以花\(a\)压缩,否则只能花\(a\)压缩
添加\(i\)进去后,我们找到后面\(dp[j]\)\(dp[i]\)转移过去,只要在\(SAM\)里跑就好了

题目四
建好自动机后,往下遍历,\(len≥k\)后则子树公共后缀都满足,染好色,区间查询就莫队

posted @ 2019-01-23 13:32  y2823774827y  阅读(157)  评论(0编辑  收藏  举报