最近学习了下kmp算法,这个算法在String中查询包含的String的效率很高,后续也有可能需要回忆和使用,这里记下自己学习后的使用心得。
一般情况下,我们查找一个长字符串(这里称该字符串为target)中是否包含某字符串(这里称该字符串为client),那么我们必须要遍历target,通过char的持续对比,如果在一个对比阶段,发现完全匹配,那么就可以判断target包含client。但是这样效率就太低了为O(m*n),而kmp的效率为O(m+n),那么kmp是如何提高效率的呢?在对比过程中target肯定是要遍历一遍的,这个是没办法避免的。那么只有从client中想办法了。
kmp通过预先分析client的规律,如果client符合某种规律,那么就有可能提高查找效率。当然如果client为“absdtf”这种的话,那么kmp效率和普通的查询就一样了。但是如果client为“ababc”或者“abcca”这种存在重复字符或者存在重复字符段的字符串,就可以提高查询效率。
kmp算法中最重要的就是找出client的规律,并通过next数组把这个规律记载下来。我们先把client设为“abababc”来进行分析。对应的next数组为{0,0,1,2,3,4,0}。
那么这个数组是怎么生成的呢?
client分为前缀和后缀(这里前缀和后缀必须相同),前后缀的最长长度即为最后1个字符对应的next数组的数值。如下图分析不同长度的字符串,可以得到不同的next值。
现在虽然我们通过眼测,把这个next的出来了。如果死板硬套,算出这个next数组,其实也没啥意义,因为这个next数组的生成方法才是kmp的算法的精髓。那么kmp是如何计算出来的呢?我现在一步步给你讲解。
ab这个数组不存在前缀后缀,也就不用说。如下:
aba的数组如下:
这里第三个a的next值(a对应的next数组值)变成了1,这个1指这个aba的前后缀长度为1。如果增加一位abax(这里定义x为未知字符),那么这个x对应的next的数值为多少呢?
假前缀为(ab),加后缀为ax,如果x为b,那么前后缀都变为真的了。而b位于client中的位置正好等于第二个a的next值。
按我理解,这个x必须与b对比,而b位于字符串client中的位置正好和a的next值相同,为1。那么我们可以这么理解:a的next值为标记tag,x字符必须与client中排序为tag的字符对比。
如果x==b的话,x的next值为前后缀的长度 + 1,也就是a的next值 + 1。而x的next值生成成功,并且为即将到来的下一位字符做好了准备。
如此,你去查看“abababc”的next值{0,0,1,2,3,4,0},是不是都可以这么解释?
那么当x与前缀结尾对比,不相等这种情况,如何解释呢?查看下面这个字符串,当x与前缀的缀尾字符不相同时,那么x必须与前缀的前缀缀尾进行对比。也就是以对比字符的next值为标记tag,x字符必须与client中排序为tag的字符对比。通过一次次对比,一直往前面跨越推进,如果都不存在,当前缀尾的next值就为0。
那么按照这种理解,编写next的生成代码,如下:
理解了next生成,其实kmp的实现跟next计算的方式差不多,如下:
kmp算法的复杂度,client和target都遍历了一遍,即为O(m+n)。
后续还会回顾这篇文章,如果你有看到这篇文章,并且对我的理解有异议,请提出,非常谢谢。