关于AC自动机的正确性的证明

没错,AC自动机是敲出来了,但是它为什么就能统计出所有的单词呢?

比如有很多串,zzhxr,zzx,hxr,hsr,shr,zhs,还有一个主串,zzxhxrszzhxrssszzhs(T),现在要统计前面的那些串有多少在主串中出现。思考一下。

首先,有一个很朴素的算法,也是一个很简单的方法,先对zzhxr,zzx,hxr,hsr,shr,zhs,这几个串,建一个字典树。设一个指针X,X从 T 的开头一直遍历到 T 的结尾,对于 X 的每一个位置,统计以所有以X开头的单词是否在字典树中出现,出现的话就就在字典树中把那个节点记录是否有单词的那个值标记为 false 。这是一个很简单的过程。我们先来证明这种方法可以找完所有的单词。

请先思考,如果有一个串(把这个串记为 A)在主串(T)中出现,那么在主串(T)中一定可以找到两个指针,*P和*Q,P指向串 A 的第一个位置,Q指向串A的最后一个位置。这个是一定的吧,这说明如果串A是出现的,那么一定能在T中找到,这说明前面说的那种方法统计出的单词的数目大于等于出现过的单词的数目,(这句话听起来很拗口,怎么可能会比出现的多呢,直觉告诉我们绝对不可能,呵呵,确实是的,但是证明的过程要求的是严谨,对于这种显而易见的结论我们也要证明)。同样的如果在主串(T)中找到一个单词,那么它必然在字典树中出现(这确实是废话),这又说明这种方法统计出的单词数目小于等于出现过单词的数目(那么他们就相等了,对吧)。综上所述,这种方法最终计算出来的就是我们需要的结果。


不急,这些只是热身,真正的证明现在开始。

我们来证明AC自动机自动AC的过程和上述方法的过程是等价的。

同样的,我们在主串(T)上设两个指针,*P和*Q,表示在字典树上已经匹配的一段字符串(ac自动机真正的实现并不是这样的,这样只是为了更好地表达),开始的时候P和Q都指向主串(T)的开始位置,Q向后移动一位,进行第一次插入,如果成功,P指针不动,并且,调用p指针的fail指针或者out指针(我的代码里是添加了out指针的,其实理论上可以不添加,添加后只是优化下时间而已),这样的话,对于可以和P和Q之间的这个串的后缀匹配的该串的所有前缀所代表的单词也会被统计出来(没办法,真的很拗口)(优化的话很简单,只要调用过的就设一个bool型标记一下,下次就不用调用了)。如果失败(设当前需要插入的字符为 cx,P指针在字典树上对应的位置为TrieP,Q在trie上所对应的位置为TrieQ),那么P指针指向 TrieP 对于字符 cx 的 fail 指针指向的位置的字符在主串上所对应的位置(不好意思这句话听起来又很拗口,但只能这么表达),同样的TrieQ的位置也会改变,但是Q的位置不变的,和前面一种情况相似,统计TrieQ在字典树上所有和它后缀匹配的前缀,处理方法和前面一样。

这样的话,对于任意的P和Q可以统计出以P-Q任意一个位置为开始,以P-Q中任意一个位置为结尾的任何串是否在字典树中匹配。如果插入成功,那么P不变,Q不断的后移,同时不断的调用out指针,满足上述情况。如果插入失败,调用TrieQ对于cx的fail指针,P移动到P’,这时说明串 P'-Q是能与P-Q的后缀匹配的最长前缀,如果在P~~~P'中存在一个位置Px满足以Px为起始位置 存在一个Qx > Q,满足串Px-Qx在字典树中匹配,那么与串 P'-Q是能与P-Q的后缀匹配的最长前缀矛盾!(想一想是为什么)

所以,以某个位置Px,(P <=Px <= P')为开头的所有的串已经被统计完。

这样,P和Q在AC自动机的调用中不断地后移的过程中,对于Px(Px<P)的所有位置为开始的能在字典树中匹配的串完全被统计。最后Q移动到主串(T)的最后一个位置,根据前面证明的,从P-Q开始到P-Q结尾的所有串也被统计,而Q后面已经没有字符了,所以,这时以P-Q为开始位置的串完全被统计。至此,AC自动机的正确性也完全被证明!


然后,AC自动机的完善

大家都知道字典树的每个节点里有很多指针是用不到的吧,但是正好在AC自动机里能用到,前面证明的时候我说调用TrieP 对于字符 cx 的 fail 指针,是的,如果插入失败的话一定是那个指针没有值,所以干脆给他赋个值,代表失败后的下一个匹配的位置,而刚刚好没有值的地方就是在这个位置所失败的地方,所以这些指针我们全部都利用起来了,是吧~~~!!这也就是我觉得AC自动机完美的地方,把一个有缺陷的字典树变成一个完美的AC自动机。完美的AC~~~~~

by zhr2010



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值