Codeforces 113BPetr#

Posted denverjin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 113BPetr#相关的知识,希望对你有一定的参考价值。

Codeforces 113 B

题意:有一个母串(S)以及两个串(S_{begin})(S_{end}),问(S)中以(S_{begin})为开头并且以(S_{end})为结尾的不同子串的个数。

思路1(后缀自动机):

首先肯定要把后缀自动机构建出来(第一次一遍敲对后缀自动机祭),然后我们考虑(S_{begin})(和(S_{end}),是对称的,不做考虑)是不是在S中作为一个子串出现,如果出现了那么看它所对应的节点是哪个,打个标记(这个操作就是在自动机上沿着(S_{begin})的每个字符的那条边走,最终走到哪个节点就是(S_{begin})所对应的节点。)

然后找S的每一个长度为(|S_{begin}|)的子串,在自动机上走一遍,如果最终的节点是(S_{begin})所对应的节点,那么就说明(S_{begin})在此出现了,在这一位上打标记。

枚举要求的子串的开头结尾位置,如果开头的时候(S_{begin})出现了,且在结尾的时候(S_{end})出现了,那么这个子串就是属于答案的。将这个子串对应的节点的这个长度的标签打一下。(这里很需要注意!!!我在写的时候直接是将这个节点的标签打上了,这样非常不对,因为一个节点的定义是长度连续的一些子串!!!如果我们只是将这个节点打上标签,则会区分不出来长度不同的子串,导致答案减少很多。因此WA15了两次!)

看每个节点打了多少标签就好了。

思路2(后缀自动机):

首先也是构造后缀自动机。

然后我们可以找到(S_{end})在自动机上对应的节点是什么。既然这个子串以 (S_{end})为结尾,那么肯定是(S_{begin})+(S_{end})的一个后缀或(S_{begin})+(一堆东西)+(S_{end})。我们希望保证(S_{begin})完整以便计算答案。所以我们需要将(S_{end})的答案加到(S_{begin})头上。

这里用的方法是这样的:

首先将end的值加到parent树的子树中(parent树就是将所有节点的link指向的值向它们自己连边后的树),

其次将所有的节点的值加到go的dag中这个节点的所有父亲们头上。

这样做的结果就是每一个节点(u)的值就是从(u)表示的某个子串开始之后以(S_{end})为结尾的不同子串个数

那么答案就应该是(S_{begin})所对应的节点。

但是如果直接这样交上去会(wa30),说明还有一些问题。

对此,freak93在最后做了一些处理,使得他的程序可以AC:

按照(S_{end})的顺序在自动机上跑,如果当前节点已经跑到了(S_{begin})应该到的位置,但是长度却不对,就是说跑到了这个起始串对应的节点的其它子串内,肯定不应该取。否则如果已经跑到了长度为(|S_{begin}|)的节点,但是并不是(S_{begin})所对应的节点,那么就肯定得跑到现在已经到的串的后缀继续看(为什么会是后缀啊。。。我觉得应该是前缀?为后面的反例埋下伏笔)

应该就是这样的?看了一天不算特别懂。。。(这里完全不对。。。

P.S.:这里的做法其实是看(S_{begin})是不是(S_{end})的一个子串且不是它的前缀,如果是的话就说明从(S_{begin})所代表的节点可以走到(S_{end})所代表的节点,

要减去一些不对的答案,比如(S_{begin})(S_{end})中出现的次数,还有如果是(S_{begin})在之前出现过,即说明真正是有答案的,他应该不更改答案,但是他成功地减去了之前求出来的的答案( imes)出现次数,所以。。。(这个方法真的很不好想啊。。。正常人不会想这种吧。。。可能他异于常人。。。

所以就被我的反例干掉了

:反例(对于freak93在CF上AC的版本):

aabaab
aa
baab

这个的答案应该是1,但是这个程序输出了0。

我感觉好像他的后缀自动机敲错了

不是他的后缀自动机错了,而是我的调试语句写错了以至于我以为后缀自动机错了。。。

肯定是他最后修改对于(wa30)的错误的步骤错了(肯定不是选择跳到后缀嘛)(错误在上面说了)

思路3(后缀数组):

先用(O(nlog^2n))naive的做法求出后缀数组(就是在正常求的每一个(2^h)的阶段都直接sort)

然后把高度数组给求出来(我也不知道什么原理。。。反正求出来的就是排名为i的后缀和排名为i+1的后缀的最长公共前缀辣)

然后暴力(这个很有个性)地求出(S_{begin})(S_{end})出现的位置们。

再想怎么补充不漏地记录答案。首先以i位置开始的子串肯定会和以i-1位置开始的子串有一段重复的,而这段重复的应该已经在i-1的时候算过了,所以i和i-1开始的后缀的最长公共前缀(高度数组)这一段不应该被i开始的子串记录。那么就很简单辣

以上是关于Codeforces 113BPetr#的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces113 D. Museum

Educational Codeforces Round 113 (Rated for Div. 2) A-C

Educational Codeforces Round 113 (Rated for Div. 2)D

[Codeforces Round #522 (Div. 2, based on Technocup 2019 Elimination Round 3)][C. Playing Piano](代码片段

代码片段:Shell脚本实现重复执行和多进程

c_cpp Codeforces片段