一. 简单的直接算法
比较次数:(n-m-1)*m次
时间复杂度O(mn)
二. Rabin-karp算法
- 算法思想:将字符串转化成数字进行粗比较,筛选后进行细比较
- 算法设计:
(1)直接数值比较
-
-
- 算法思想:字符集与 1-n 的数值满足双射,字符串转化为n进制数值
- 优化程度:a. 比较次数:n-m次。
-
b. 计算次数:1 + n-m 次
-
-
- 问题:m位数的计算复杂,优化程度低
-
(2)hash函数数值比较
-
-
- 算法思想:将较大的数值用hash函数映射到较小的数
- 优化方法(假设n=10):
-
预处理:fp = p[ m-1 ] + 10*(p[ m-2 ] + …+ 10*(p[ 1 ] + 10*p[ 0 ] ))) mod q。
移位重新计算:
-
-
- 复杂度分析:当q是素数时,平均:O(n+m),最坏:O(mn)
-
三.BM算法
1. 算法思想:
(1)从后往前匹配的好处:在比较过程中不匹配的概率远远大于匹配的概率,基于这一特点,在匹配失败时,从后往前匹配的跳跃位置更大
(2)每次发生不匹配,根据好后缀规则和坏字符规则(规则都是对模式串而言的)跳转到下一次比较的位置
2. 算法设计
(1)预处理:(对模式串T进行预处理)P[ 0-n ]
坏字符规则:后移位数 = 失配位置 - 模式串上一次出现坏字符的位置(未出现记为 -1)
好后缀规则:后移位数 = 好后缀的位置 - 模式串上一次出现好后缀的位置(未出现记为 -1)
(2)从左到右移动,从后往前比较,失配时移动位置 = max{ 坏字符规则 , 好后缀规则}
四. BMH算法
1. 算法思想:
总体上是BM算法的改进,关注点在模式串的最后一个字符,规则更简单
2. 算法设计
(1)预处理(对字符集/文本串预处理)
偏移表:P[ 0-m-1 ]
(2)从左到右移动,从后往前比较
五. KMP算法---最坏情况依旧是线性
1. 算法思想:
文本中的每个字符仅匹配一次,充分利用已经匹配的部分做跳转
2. 算法设计
(1)计算最长公共前后缀:maxL
关于如何计算最长公共前后缀(例如P=abaabcac):
A.暴力算法:
得到所有前缀和后缀,分别比较。
B. 递推算法:
得到P的所有前缀,第一个前缀的maxL初始化为0。
假设已经求得第 i 个前缀的maxLi = k , 求解第 i + 1 个前缀的maxL(i+1)
若 P[ k ] == P[ i ],maxL(i+1)= k+1;
否则,使用暴力算法计算maxL.
例:
a 0
ab 0 递推
aba 1 递推
abaa 1 递推
abaab 2 递推(举例1)
abaabc 0 暴力(举例2)
abaabca 1 递推
abaabcac 0 暴力
maxL = { 0、0、1、1、2、0、1、0}
(举例1)第四前缀abaa:maxL = 1,又P[ 1 ]==P[ 4 ] == b,则第五前缀abaab:maxL = 2
(举例2)此时不满足P[ k ] == P[ i ],暴力求解,
得到abaabc的所有前缀和所有后缀,对应比较
abaabcabaabc
a c 0
ab bc 0
aba abc 0
abaa aabc 0
abaab baabc 0
则第六前缀abaabc:maxL = 0
(2).将maxlL整体右移一位,初始为 -1,得到next数组
next = { -1、0、0、1、1、2、0、1}
(3)从左到右,从前往后匹配,失配时,将 next数组对应的值作为下标与失配位置对齐(j = next[ j ])