[复习]字符串
[复习]字符串
纯复习内容,内容比较粗糙。
字符串哈希
最基本的东西,一般而言并不太需要注意哈希被卡的问题。
个人比较习惯的是单哈希、自然溢出。偶尔会使用多模数哈希,但还是用自然溢出。
可以用来干的事情:快速判断两个串是否相等,判断回文串等。
比较容易实现,不多写了。
最小循环表示法
\(lun\)讲过了好多次了嗷。。。。
这个东西是啥?参考一道题目【洛谷1368/BZOJ2882】工艺。
显然存在一个直接构建\(SAM\)然后按照字典序跑的做法。
这里有个简单的做法:定义两个指针\(i,j\)。初始时\(i=1,j=2\)。
然后两个指针不断向后匹配,直到扫到两个字符不相等,假设前面相等的长度为\(k\)。
如果\(S[i+k]<S[j+k]\),那么显然\(j\)在这一段之间一直不优,所以令\(j=j+k\)。否则\(i=i+k\)。
如果做完上述操作之后两个数相等,则强制一个向后移动即可。
\(KMP\)算法
很久以前写的博客
重新谈谈。
\(KMP\)的\(next\)数字到底是啥呢?\(border\)。即前缀等于后缀。
\(next\)的含义就是以当前位置结尾的最长\(border\)。
知道了这个那么\(next\)数组很容易求,假设当前要求的结尾位置为\(i\)。那么它可能达到的最长\(border\)为\(next[i-1]+1\),如果可行就做完了。否则只能找\(i-1\)的\(border\)的最长\(border\)了,那么就循环处理。最后的特殊情况下\(border\)可以为\(0\)。
利用\(next\)匹配求解答案的时候类似,一旦匹配不上就考虑能否从\(border\)位置开始匹配。
\(AC\)自动机
显然原来也是写过的
再来口胡一遍。
AC自动机可以理解为是\(KMP\)的多串版本。
大致的做法是把所有串构建\(Trie\),定义\(fail\)指针表示当前节点表示的串中的在\(Trie\)树上的最长\(border\)的位置。这样子每次失配的时候直接暴跳\(fail\)即可。
\(fail\)指针的构建方法是\(bfs\)。
一般而言会有两种不同的\(AC\)自动机,一种就是最普通的,另外一种是\(Trie\)图。\(Trie\)图是所有转移即目标节点构成的图。如果需要在\(AC\)自动机上面进行\(dp\)相关操作,\(Trie\)图是更加合适的选择。
然后大概归类一下\(AC\)自动机的相关题目类型:
首先是纯匹配类型的,完全就是看自己对于\(AC\)自动机的理解,比如这题、还有这题。这类题目很多,专注于对于\(AC\)自动机的理解是关键。
第二类是\(AC\)自动机上面\(dp\)。而\(dp\)的问题一般是一些串不能出现在答案串中的方案数,或是必须出现在答案串中的方案数之类的。做法就是构建\(Trie\)图,然后在上面匹配。如果是不能出现,那就是有些点不能被访问到,如果是必须出现,那么就状压记录串的出现情况。转移沿着\(Trie\)图走就行了,并且如果串长之和很小还可以矩乘。题目随便找几道吧。这个、这个、这个。
第三类是和\(fail\)树相关的东西,这一类单独拿出来是因为后面的\(SAM\)和\(PAM\)都有这一类的问题。主要是这题。利用\(fail\)树加上一些统计子树内询问的方法来解决问题。
其他的还有一些奇怪的东西,比如二进制分组+\(AC\)自动机之类的,这里就不提及了。
\(manacher\)
原来都写过啊 。
维护回文中心,利用回文的形式来快速求解。
代码和思想都比较简单,用途不是很多,就不讲了。
后缀数组
戳这里
按照二元组的倍增排序。
后缀数组能够做的,\(SAM\)基本都能做,当然也有些东西\(SAM\)做起来没那么方便。
所以\(SA\)还是要会的。
为了复习随手写了道题目戳这里。
回文树
似乎回文树就是回文自动机吧?原来也写过QwQ
关于回文串的一些计数问题,回文树和\(manacher\)各有利弊。回文树求出来的是以当前位置结尾的回文后缀的个数,而\(manacher\)求出来的是每个回文中心能够延伸出去的回文半径。用哪个因题而异。
是在是找不到太多很有意义的题目。
不过有一道和回文树性质相关的题目。【CF932G】。
给回文树中每个节点定义一个\(dif\),表示其和父亲的\(len\)的差。
定义\(anc\)表示所有祖先中,第一个与其\(dif\)不相同的祖先。
可以证明把当前点到根节点的所有\(dif\)拿下来一定是一段段的等差数列。并且每个点如果沿着\(anc\)走到达根节点的次数不会超过\(log\)次。对于沿着回文树\(fail\)树上的\(dp\)转移,可以利用上述性质优化。
这个玩意支持前端插入后端插入,前端删除,后端删除,但是不常见,所以不太会。
后缀自动机
戳这里
板子什么的背背就好了。但是每个节点表示的是什么,性质之类的,一定要记牢。
毕竟这个玩意的运用太广了。
然后如果只要求解\(right\)集合的大小的话,拓扑序向上合并就好了。
如果要求\(right\)集合的话,线段树合并维护。
还有如果要支持动态维护\(right\)集合大小的操作的话可以\(LCT\),题目似乎是【BZOJ2555】。
还可以套上各种各样的东西。
还有就是两个串的\(lcp\)就是他们在\(SAM\)上所代表的节点在\(parent\)树上的\(LCA\)的\(len\)。
主要就这些吧。
这里的变化还是挺多的。