暴力匹配算法
定义:
从主串的第一个位置开始,与模式串逐个对齐,挨个比较.若匹配失败,则从主串的第二个位置继续进行匹配,以此类推,直到匹配成功或到达主串的结尾。
举例:
主串:CDCCDCCDDCCDDC
模式串:CCDDCCD
比较过程:
1.第1次匹配
CDCCDCCDDCCDDC
√X
CCDDCCD
2.第2次匹配
CDCCDCCDDCCDDC
X
CCDDCCD
3.第3次匹配
CDCCDCCDDCCDDC
√√√X
CCDDCCD
...
...
6.匹配成功
CDCCDCCDDCCDDC
√√√√√√√
CCDDCCD
缺点:
暴力算法固然好用,但是如果是下面这种情况:
主串:CCCCCCCCCCCCD
模式串:CCCCCD
比较过程:
1.第1次匹配
CCCCCCCCCCCCD
√√√√√X
CCCCCD
2.第2次匹配
CCCCCCCCCCCCD
√√√√√X
CCCCCD
3.第3次匹配
CCCCCCCCCCCCD
√√√√√X
CCCCCD
...
...
8.匹配成功
CCCCCCCCCCCCD
√√√√√√
CCCCCD
那么对于这两个字符串的匹配,能不能只让主串与字符串的第1次匹配过程中依次比较,而第2次到第8次的匹配过程中只需比较模式串最后1个字符和其对应的主串字符是否比对呢?我们可以通过KMP算法达到目的
KMP算法
理解1:
定义数组:A[10],B[6]
主串:A[0-9]
模式串:B[0-5]
1.第1次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第6次比对失败,得出:
A[0]=B[0]
A[1]=B[1]
A[2]=B[2]
A[3]=B[3]
A[4]=B[4]
A[5]!=B[5]
2.第2次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第1次匹配失败后模式串右移1位后成功匹配,得出:
A[1]=B[0]
A[2]=B[1]
A[3]=B[2]
A[4]=B[3]
A[5]=B[4]
A[6]=B[5]
3.结论:
结合第1次和第2次匹配得出:
A[1]=B[0]=A[0]
A[2]=B[1]=A[1]
A[3]=B[2]=A[2]
A[4]=B[3]=A[3]
A[5]=B[4]=A[4]
A[6]=B[5]
是匹配失败后模式串右移1位匹配成功的充要条件
显然B[0]=B[1]=B[2]=B[3]=B[4]
是匹配失败后模式串右移1位匹配成功的必要条件
1.第1次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第6次比对失败,得出:
A[0]=B[0]
A[1]=B[1]
A[2]=B[2]
A[3]=B[3]
A[4]=B[4]
A[5]!=B[5]
2.第2次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第1次匹配失败后模式串右移2位后成功匹配,得出:
A[2]=B[0]
A[3]=B[1]
A[4]=B[2]
A[5]=B[3]
A[6]=B[4]
A[7]=B[5]
3.结论:
结合第1次和第2次匹配得出:
A[2]=B[0]=A[0]
A[3]=B[1]=A[1]
A[4]=B[2]=A[2]
A[5]=B[3]=A[3]
A[6]=B[4]=A[4]
A[7]=B[5]
是匹配失败后模式串右移2位匹配成功的充要条件
显然B[0]=B[2],B[1]=B[3],B[2]=B[4],B[3]=B[5]
是匹配失败后模式串右移2位匹配成功的必要条件
1.第1次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第6次比对失败,得出:
A[0]=B[0]
A[1]=B[1]
A[2]=B[2]
A[3]=B[3]
A[4]=B[4]
A[5]!=B[5]
2.第2次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第1次匹配失败后模式串右移3位后成功匹配,得出:
A[3]=B[0]
A[4]=B[1]
A[5]=B[2]
A[6]=B[3]
A[7]=B[4]
A[8]=B[5]
3.结论:
结合第1次和第2次匹配得出:
A[3]=B[0]=A[0]
A[4]=B[1]=A[1]
A[5]=B[2]=A[2]
A[6]=B[3]=A[3]
A[7]=B[4]=A[4]
A[8]=B[5]
是匹配失败后模式串右移3位匹配成功的充要条件
显然B[0]=B[3],B[1]=B[4]是匹配失败后模式串右移3位匹配成功的必要条件
1.第1次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第6次比对失败,得出:
A[0]=B[0]
A[1]=B[1]
A[2]=B[2]
A[3]=B[3]
A[4]=B[4]
A[5]!=B[5]
2.第2次匹配
A[0]A[1]A[2]A[3]A[4]A[5]A[6]A[7]A[8]A[9]
B[0]B[1]B[2]B[3]B[4]B[5]
假设第1次匹配失败后模式串右移4位后成功匹配,得出:
A[4]=B[0]
A[5]=B[1]
A[6]=B[2]
A[7]=B[3]
A[8]=B[4]
A[9]=B[5]
3.结论:
结合第1次和第2次匹配得出:
A[4]=B[0]=A[0]
A[5]=B[1]=A[1]
A[6]=B[2]=A[2]
A[7]=B[3]=A[3]
A[8]=B[4]=A[4]
A[9]=B[5]
是匹配失败后模式串右移4位匹配成功的充要条件
显然B[0]=B[4]是匹配失败后模式串右移4位匹配成功的必要条件
理解2:
定义:
定义两个指针i,j
i扫描主串,j扫描模式串
二者分别指向主串和模式串的第1个位置
举例:
主串:ABABBBAAABABABBA
模式串:ABABABB
Si表示前一次i与j扫描到同位置的状态,
Si+1表示后一次i与j扫描到同位置的状态
匹配过程是重复的由Si推进到Si+1的过程
1.保留主串和模式串
Si状态下:
123456789-------
ABABBBAAABABABBA
√√√√X
ABABABB
1234567
123456789-------
ABABBBAAABABABBA
X
ABABABB
1234567
Si+1状态下:
123456789-------
ABABBBAAABABABBA
√√?
ABABABB
1234567
2.仅保留模式串
Si和Si+1状态下:
12 34 5 67
AB AB A BB
FL FR j
FL j
AB A BABB
12 3 4567
这里可以看出:如果让Si直接跳转到Si+1,而不经过中间的匹配过程,只需要模式串前移,能够使FL和FR重合即可