个人水平所限,只能谈及几年前的OI省选水平的算法。再高深的,便不清楚了。
主要参考文献:
- hzwer,《OI省选算法汇总》。
- _Rayx,《多种字符串匹配算法杂谈》。
- airfer,《字符串匹配算法比较》。
- 罗穗骞,《后缀数组——处理字符串的有力工具》。
- xiazdong,《Dijkstra、Bellman_Ford、SPFA、Floyd算法复杂度比较》。
- Create Chen,《理解A*寻路算法具体过程》。
- lufy,《A*寻路算法与它的速度》。
- Amit,蔡鸿译,《Amit's A star Page中译文》。
OI算法,大抵可分为以下几类:
- 数据结构
- 字符串相关
- 图论相关
- 数学相关
- 动态规划
- 计算几何
- 不保证正确性的算法
- 其他重要工具与方法
下面依次谈一谈。
这个算法是怎么来的,是为了解决什么问题而来,以前的算法为什么没有解决,而现在的算法却能解决;现在这个算法还有些什么缺点;能做一些什么问题,不能做些什么问题。—— Ronghua Li
字符串相关
单模式串匹配
-
KMP:在主串S(长度N)中匹配一个模式串P(长度M),预处理时间 O(M) ,匹配时间 O(N+M) 。
-
KMP及相关算法时间复杂度比较:
其中,M为模式串长度,N为主串长度。 实测时,string长度10000,每个函数都被调用了1000次。
算法 | 预处理时间 | 匹配时间 | 实测时间 |
---|---|---|---|
BF | O(0) | O(NM) | 0.078 |
KMP | O(M) | O(N) | 0.094 |
BM | O(N+M2) | O(N) | 0.047 |
Sunday | O(M) | O(NM) | 0.172 |
Robin-Karp | O(0) | O(NM) | 0.328 |
Bitap | O(M) | O(NM)→O(N) | 0.281 |
字典树(Trie)
Trie是一种n叉树,n为字母表大小,每个节点表示从根节点到此节点所经过的所有字符组成的字符串。
多模式串匹配
- AC自动机:在主串S(长度N)中匹配多个模式串P(长度M),预处理时间 O(N+∑Mi) ,匹配时间 O(N+∑Mi) 。
AC自动机就是KMP思想。但用KMP做多模式串匹配的时间复杂度是 O(∑N+Mi) 。显然,提高的复杂度是 O(N(K−1)) ,其中K表示模式串的个数。而当模式串数量大、模式串较短、主串较长时,算法几乎是从 O(N2) 降到了 O(N) 。
还有一种多模式的匹配算法叫做AC自动机。它能一次匹配多个模式串。它与KMP的思路很像,不匹配时找一个最长的再继续进行!它需要先把字符串建成一颗Trie树,树结点有一个叫做failed的指针,是表示如果不匹配时应该再从哪个结点进行匹配。因为这种做法是一种DFA上的匹配,而发明这种算法的人叫A.C.,所以就叫AC自动机了。复杂度很好,比每个模式串用一次KMP算法要好很多。
后缀树、后缀数组
- 这俩是非常有用的字符串处理工具,尤其是后缀数组,很多复杂字符串问题都可以用它来快速完美解决。
- 构造:倍增算法 O(logN) ,DC3算法 O(N) ,但前者常数小一些,且实现较容易。
- 内容:
- 后缀数组,SA[1~N],表示“排第几的是谁?”。也就是将S的N个后缀升序排序,把排好序的后缀的开头位置顺次放入SA中。显然,其满足:
Suffix(SA[i])<Suffix(SA[i+1])
- 名次数组,Rank[1~N],表示“你排第几?”。也就是 Suffix(i) 在所有后缀中升序排序的名次。
- 后缀数组,SA[1~N],表示“排第几的是谁?”。也就是将S的N个后缀升序排序,把排好序的后缀的开头位置顺次放入SA中。显然,其满足:
- 用途:
- 最长公共前缀
- 单个字符串的相关问题
- 重复子串
- 可重叠最长重复子串
- 不可重叠最长重复子串 (pku1743)
- 可重叠的最长重复子串 (pku3261)
- 子串的个数
- 不相同的子串的个数 (spoj694, spoj705)
- 回文子串
- 最长回文子串 (ural1297)
- 连续重复子串
- 连续重复子串 (pku2406)
- 重复次数最多的连续重复子串 (spoj687, pku3693)
- 重复子串
- 两个字符串的相关问题
- 公共子串
- 最长公共子串 (pku2774, ural1517)
- 子串的个数
- 长度不小于k的公共子串的个数 (pku3415)
- 公共子串
- 多个字符串的相关问题
- 不小于k个字符串中的最长子串 (pku3294)
- 每个字符串至少出现两次且不重叠的最长子串 (spoj220)
- 出现或反转后出现在每个字符串中的最长子串(pku3294)
还有一种叫做后缀数组和后缀树的,后缀树是可以转发为后缀数组的,这两种构造起来很不简单,但是复杂度却是惊人的好。如求最长重复连续子串,出现次数最多的子串等都能用它完美的解决。有兴趣的可以搜搜,后缀数组的资料应该是比较多的,而后缀树由于太复杂,资料不是很多,还是有的。
关于后缀数组构造的倍增算法,有一个特别好玩的小故事,大家可以去看看~
《后缀树,后缀数组,离散化》,去看73到81页,可爱的小白兔们^_^
模糊匹配
上面介绍的都是精确匹配的算法,其实对于字符串,还有一种模糊匹配,有兴趣的读者可以阅读一本叫做《柔性字符串匹配》的书,肯定会让你获益匪浅。
其他
- 后缀自动机
- Manacher