KMP算法产生的背景:文本匹配问题
什么是文本匹配:在一个被称为主串的文本中,查找是否存在一个被称模式串的文本,比如你要在“今天是星期三”这样一个文本中,搜索“星期三”,这个问题看起来很傻,但它的应用很广泛,比如你在百度搜索信息的时候,就是在进行这样的文本匹配,只不过匹配次数非常多。
什么是KMP算法:一种高效的字符串(文本)匹配算法
为什么KMP算法高效:要理解这个,必须先了解一下什么是低效率的字符串匹配算法。
低效率的字符串匹配算法:朴素(暴力)字符串匹配算法,是一种简单,而又相对耗时的匹配算法,内容大概是:从主串的第一个字符开始,逐个的去和模式串进行对比,每当匹配失败,就返回主串的下一个字符,再开始匹配,直至主串结束。
Example:在主串aabcaaaac中寻找aaac
可以看出这是一个简单粗暴的方法
为什么朴素字符串匹配算法低效:因为它进行了很多回溯。
KMP算法是怎么运作的:要了解这个问题,首先我们要学习一下最长相等前后缀的概念
最长相等前后缀:一个字符串中,除去最后一个字符,前面任意长度的串,被称为前缀。除去第一个字符,后面任意长度的串,被称为后缀
例如,12345这个串,它的前缀可以是 1 、12 、123、1234,它的后缀可以是5,45,345,2345。
最长相等前后缀就是前缀和后缀中相等且最长的那一个
例如ababab的最长相等前后缀是abab
KMP算法是怎么减少回溯的:通过利用最长相等前后缀长度的概念。
next数组:每个值就意味着你在模式串的第n个位置匹配失败时,应该回溯到哪个位置。
如果下标初始值为0,Next[i]的值的意思是,当在下标为i的位置,即第i+1个字符发生匹配失败时,应该回到下标为next[i]的位置,即第next[i]+1个字符
如果下标初始值为1,Next[i]的值的意思是,当在下标为i的位置,即第i个字符发生匹配失败时,应该回到下标为next[i]的位置,即第next[i]个字符
example
此时下标初试值为0,当你在模式串的第一个字符就匹配失败时,next值为-1,并不意味着你要把下标移到-1,根本就没有-1,而是要进行一个特殊操作:主串移到下一位,模式串移到第一个。
当你的模式串在第6个字符匹配失败时,也就是下标为5的位置,此时next[5]就意味着前5个字符的最长相等前后缀长度是3,同时也意味着你要把模式串的指针移到下标为3的位置,即第3+1个元素。
再来看看下标初始值为1是什么情况
此时下标初试值为1,当你在模式串的第一个字符就匹配失败时,next值为0,并不意味着你要把下标移到0,根本就没有0,而是要进行一个特殊操作:主串移到下一位,模式串移到第一个。
当你的模式串在第6个字符匹配失败时,也就是下标为6的位置,此时next[6]就意味着前6-1个字符的最长相等前后缀长度是3,同时也意味着你要把模式串的指针移到下标为4的位置,即第4个字符。
KMP算法高效的体现。
同样还是刚才的例子
虽然快的不多,但是依然比暴力算法少了三次比对。可见只有在主串存在存在比较多相似于模式串的情况下,Kmp算法才会比暴力算法快很多。
getnext算法例子1
例子2
实际上KMP算法还存在缺陷
看如下例子
我们已知,主串当前是在c,且模式串当前不匹配位置是a,我们也知道模式串将会回溯到第二个位置,而第二个仍然是a,按道理来说没有对比的必要。
如何优化?采用nextval数组
做法 逐个检查 T[i] 与T[next[i]]的值是否相同,若相同,将next[i]的值置为next[next[i]]的值,若不相同,则保持原值