深入浅出谈谈KMP算法
关于KMP算法,我将会讲解理论以及如何解决实际问题。
首先,KMP算法本身的名字并没有什么实际含义,就是发明这种算法的三个科学家的首字母。然后,我们要知道这个算法,解决的实际上就是字符串的匹配问题。
比如,我们有两个字符串A,B,它们分别包含M和N个元素。假设M>N,为了知道字符串A中是否包含字符串B,最暴力的解决方法就是通过两层for循环,将B字符串挨个和A字符串相匹配。我们可以知道,由于时间复杂度考虑的是最坏的情况,所以如果我们运气最坏的话,将会紧贴着A字符串的末尾找到B字符串。时间复杂度即O(M*N)。
如果字符串很小,那么暴力解决法当然方便。但大多数时候,为了提高代码的性能,我们通常都需要采用一种更为复杂的方式,来提高代码的运行速度。这时,就有了KMP算法。虽然它的实现比普通的暴力解决法复杂许多,却可以大大减小代码运行的空间复杂度。
为了理解KMP算法,首先我们要了解的就是前缀表。每一个字符串,都有其对应的前缀和后缀。其中,前缀指的是包含首字母,但不包含尾字母的所有子串;而后缀,就是包含尾字母,但不包含首字母的所有子串。例如,有字符串abcde,它的前缀有a,ab,abc ,abcd,它的后缀有e,de,cde,bcde。
有了前缀和后缀的概念,接下来我们要介绍的就是最为关键的:最长公共前后缀,又称为最长相等前后缀。例如,有 abcdabcf 这一串字符,我们要求出每个字母所对应的最大相等前后缀。其中,每个字母分别对应了a,ab,abc,abcd,abcda,abcdab,abcdabc,abcdabcf 这八个字符串。
其中,a 比较特殊。由于只有一个字母,既是头又是尾,没有相等前后缀。然后可以看出,一直到abcd, 都没有出现相等前后缀。随后三个字符串,最大相等前后缀分别为 a,ab,abc,大小分别为 1,2,3。最后一个字符串 abcdabcf ,不存在最大相等前后缀。然后,我们就可以得到字符串的每一个字母所对应的最大相等前后缀的大小,分别是{ 0,0,0,0,1,2,3,0 }。而这,就是所谓的前缀表。
但是,我们如何将前缀表和两个字符串的匹配问题结合起来呢?我们来看上图的例题:字符串A是 abcdabcdabcf ,而字符串B是 abcdabcf,字符串B到f之前,都可以与字符串A相匹配,我们可以看见,f前面的字符串所对应的最大相等前后缀就是3,然后我们找到3下标对应的字母为d,再从d开始往下匹配。
为什么要这么设置呢?我们观察可以发现,断点f前面所对应的字符串abcdabc的最大相等前后缀为3,又abcdabc已经与字符串A相关匹配,所以我们就可以将已经匹配过的第二个abc在新一次匹配中重复利用起来,从而减少匹配的次数。这就是KMP算法有利于减小时间复杂度,从而提升代码速度的底层逻辑。