高效字符串匹配 KMP算法解析

应用场景

在淘宝这个处理数据的平台上经常进行字符串的匹配,查找,替换等操作,尤其对搞搜索的同学来说非常的重要。KMP算法由Knuth、Morris、Pratt 同时提出来的,取了这三个人的名字的头一个字母,所以叫做KMP算法。

原理分析

KMP算法主要利用模式串(要匹配的子字符串)自身的对称特点来节省比较的次数。先看看一般的字符串匹配算法。主串A=abcdabcdefg,子串(模式串)B=abcde。

第一步A=abcdabcdefg 从首字母开始比较,一直比较到A[4]!=B[4] 比较失败。

B=abcde

第二步A=abcdabcdefg A串从A[1]开始,B串从B[0]开始 比较 一直到A[1]!=B[0]。

B=abcde

第三步A=abcdabcdefg A串从A[2]开始,B串从B[0]开始 比较 一直到A[2]!=B[0]。

B=abcde

一直循环下去直到在A串中找到与B串一样的子串,这种算法的时间复杂度是O(mn)。

再看看KMP算法:

KMP算法需要两个数组游标指针A串的A[i]游标指针i,B串的B[j]游标指针j。

主串A=ababadef ,子串B=ababag

i 0 1 2 3 4 5 6 7 ……

A a b a b a b e f ……

B a b a b a g

j 0 1 2 3 4 5

第一步 i和j 同时自增1,比较A[i]和B[j]。直到i==5,j==5时 A[i]!=B[j]。

i 0 1 2 3 4 5 6 7 ……

A a b a b a b e f ……

B a b a b a g

j 0 1 2 3 4 5

一般的算法在遇到A[i]!=B[j]时 i回溯到i==1,j回溯到j==0然后再比较,但KMP算法的i 不回溯到i==1,而是让j 改变到j1 ,再进行第二步A[i]与B[j1]的比较。

第二步让j 改变到j1 ,进行A[i]与B[j1]的比较。j1 ==4 。这时A[i]==B[j1],为什么j 变成j1==4 对于理解KMP算法非常重要。因为子串B的子串{B[0]….B[4]} 有对称性,B[0] B[1] B[2]== B[2] B[3] B[4]。j1 就是要在B串的头j1个字符中找到这样对称的字符串,当然j1越大越好。 正是利用这样的对称性 就相当于从A[2]与B[0]开始比较,又因为A[2] A[3] A[4]== B[0] B[1] B[2] 所以不需要将i==5回溯到i==1直接进行符A[5]与B[3]的比较。就这样比一般算法节省了i回溯到i==2,i==3,i==4的比较的那些次数。要想节省这些比较次数就的在B串中找到尽可能长的从B[0]开始对称串,就是找到尽可能大的j1 使得{B[0]……B[j1-1]}=={B[j1-1]……B[j-1]}。

i 0 1 2 3 4 5 6 7 ……

A a b a b a b e f ……

B a b a b a g

j 0 1 2 3 4 5

第三步当比较到A[6]!=B[4]时 i==6,改变j为j1=1,{B[0]……B[j1]}=={B[j1+1]……B[j-1]}就是{B[0]B[1]}=={B[2]B[3]}。

i 0 1 2 3 4 5 6 7 ……

A a b a b a b e f ……

B a b a b a g

j 0 1 2 3 4 5

这时发现仍然A[6]!=B[2]。

第四步 由于仍然A[6]!=B[2]继续改变j为j1==0。{B[0]……B[j1]}=={B[j1]……B[j-1]}就是{B[0] }=={B[0] }。

i 0 1 2 3 4 5 6 7 ……

A a b a b a b e f ……

B a b a b a g

j -1 0 1 2 3 4 5

这时发现仍然A[6]!=B[0]。继续移动i,让i自增,直到再重复前面四步。

下面再来看j1是如何预先计算出来的。可以用一个数组P[j]来记录j1。因为P[j]与主串没有关系,是由模式串自身的特征决定的,所以可以预先计算出来。

j==0的时候B的子串为B[0]=a 又因为B[0]== B[0](对称)所以P[0]==-1(下标为-1表示B的首字符前一个假想的没有的字符)。

j==1的时候 B的子串B[0]B[1]=ab 向前找到对称串找到B[0]== B[0]所以P[1]==-1。

j==2的时候B的子串B[0]B[1]B[2]=aba向前找对称串,找到B[0]==B[2]所以P[2]==0

j==3 的时候B的子串B[0]B[1]B[2]B[3]=abab向前找对称串,找到B[0]B[1]== B[2]B[3]所以P[3]==1。

j==4的时候B的子串B[0]B[1]B[2]B[3]B[4]=ababa向前找对称串,找到B[0]B[1]B[2]== B[2]B[3]B[4],所以P[4]==2。

j==5的时候B的子串B[0]B[1]B[2]B[3]B[4]B[5]== ababag向前找对称串,找到B[0] == B[0],所以P[5]==-1。

B a b a b a g

j -1 0 1 2 3 4 5

P[j] -1 -1 0 1 2 -1

由此来确定P[i]数组。

代码示例

public class TestKMP

{

char[] A={‘a’,’b’,’a’,’b’,’a’,’b’,’e’,’f’};

char[] B={‘a’,’b’,’a’,’b’,’a’,’g’};

public int[] P = new int[6];

public void initP()

{

int i=-1;

int j=0;

P[0]=-1;

while(j<B.length)

{

if( -1 == i || B[i] == B[j])

{

i++;

j++;

if( j < B.length )

{

P[j]=i;

}

}

else

{

i=P[i];

}

}

}

public int processKmp()

{

int i=0;

int j=0;

int lA=A.length;

int lB=B.length;

while( i < lA && j < lB )

{

if( -1 == j || A[i] == B[j] )

{

i++;

j++;

}

else

{

j=P[j];

}

}

if( j == lB )

{

return i-lB;

}

else

{

return -1;

}

}

/**

* @param args

*/

public static void main(String[] args)

{

TestKMP testKMP = new TestKMP();

testKMP.initP();

System.out.println(testKMP.processKmp());

}

}

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/iteye_2332/article/details/82058819
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞