串
以下笔记为jjyaoao本人制作,欢迎借鉴学习和提出相关建议,转载需要标明出处www.jjyaoao.space
串
ADT
定义和特点
术语
统一约定:
联系:在i+k长度的前缀中,长度为k的后缀
任何串,也是他自身的子串,前缀,后缀,长度严格小于原串的子串,前缀,后缀,也成为了真子串,真前缀,真后缀
接口定义
模式匹配
问题与需求
算法评测
蛮力匹配
构思
版本一
版本二
复杂度分析
但是就期望而言,对于日常使用,往往可以每一个位置都达到o(1),总共累计不过线性O(n)复杂度
我们不满足于次,想要把期望抹掉,好消息是这类算法的确存在,比如下面的知名的KMP
KMP算法:记忆法
阐述
蛮力为何低效
不变性
记忆力
预知力
查询表
制表备查
主算法
实例
理解Next[]表
快速移动
避免回溯
t越小,对应的位移越大,反之,对应的位移越小,而位移量更小,就意味着更安全,这也暗示了另一个不变性,也就是由KMP舍弃的哪些位置,的确是不值得对齐的
通配哨兵
当j<0时,通过通配的哨兵结点,使得T[i] == T[j] 必然成功,从而使模板表位置回到第一位
构造Next[]表
递推
算法
显而易见,该过程也就是next自己与自己逐一比对的过程,(哨兵使得算法鲁棒性良好)我们可以通过修改KMP的框架简要得出模板表的代码
实现
1 | int* buildNext ( char* P ) { //构造模式串P的next表 |
实例
分摊分析
失之粗糙
通过过程分析
精准估计
根据代码主体分析
若对k为何这样取,看习题解析(下次一定
再改进
美中不足
这种反例的情况,如果说第一次的比对还是有意义的话,那么后面的比对都是多余的。
以卵击石
为何一错再错???Next[]表。Next[]表为整个算法的灵魂所在
把1比作石头,0比作鸡蛋
前车之鉴
我们在if里增加一句判断语句,新的字符必须与此前那个字符不一样,新的字符不一定会比石头更硬,但他至少不应该仍然是一枚鸡蛋
实例
可视对比
每一个区间的计算成本,都可以用深色区域来度量
蛮力算法
KMP
BM_BC:以终为始
善待教训
前轻后重
我们在后面获得教训,利用价值往往更大(排除更多的位置)
我们获得失败的概率也远远大于成功的概率
以终为始
坏字符
这一策略,将我们之前称为的教训,转名为 坏字符
当出现坏字符,使我们在这次匹配能获得成功带来失望,所以说是坏字符
特殊情况
实例
构造BC[]
性能分析
最好情况
适配概率越大越好
最坏情况
BM_GS:好后缀
兼顾经验
好后缀
制表
实例体验
构造GS表
构造MS[]
构造SS[]
构造GS[]
性能分析
各算法总览
KMP:适用于规模小的情况 适合容易匹配到的情况
BC:适用于大字符集 适合容易匹配不到的情况
BC+GS:无敌
Karp-Rabin:串即是数
化串为数
凡物皆数
串亦是数
数位溢出
散列压缩
应对冲突
只要有散列,就会有冲突
因此,一旦检测到散列码一样,我们也不会贸然的选取,这里采用的是,散列码一样之后,在进行逐位比对