KMP算法

来源:互联网 发布:java 蓝牙通信协议 编辑:程序博客网 时间:2024/06/04 00:22

KMP算法通常用来匹配字符串,时间复杂度为O(n+m)。一般来说,我们匹配字符串,首先想到的是对源字符的每个位置,都进行匹配。但这样花费的时间过多,时间复杂度为O(n*m),虽然在实际中,也可以接近O(n+m)。KMP算法采用了一个数组来保存元素的位置,这样就可减少匹配的次数(是不是和DP有些类似呢)

下面我们来说一下,它的实现思路,假设,有两个字符串源字符串R,目标字符串T,用来保存T中每个元素位置的数组next[],设匹配从R的第i个元素开始(我们以下标1表示第一个元素),第一次查看T的第j个元素(这里为第1个)是否与i相同,若相同j++,继续匹配。若不同,令j = next[ j ],然后继续匹配,直到j = 1。若还是不同,则令i++,j++,然后继续匹配,如此循环直到超出R或T的长度。

下面说一下它是如何减少匹配次数的,关键就在于next[],当我们发现有不匹配元素,不要急着将i的还原为匹配前的下一个,而是偷一下懒,先看一下T中是否有比当前j小,且与当前位置“等价”的位置,这里的意思是在T中存在着两段小数组t1,t2。t1与t2相等,且t1以T的第一个元素为开头,t2以“T的最后一个元素”为结尾(实际上以第j-1个元素结尾)。若存在t1,t2,则j = t1.length+1,即让t1后的第一个元素与R中的第i个元素进行比较。若相同,则比较下一元素,若不同,则再次进行此操作。这好像将T字符串,往前推(R不动),改为将R字符串往后推(T不动)。实际上,将T往后推,节省了大量的比较操作。而我们在next[]中存储的就是每个T元素的t1.length+1;这样当在第j个元素处不相同时,就可以直接从数组拿来使用。

下面我们再来说一下next[]的实现。首先,我们让它的长度为T.length+1,因为我们要以下标1表示第一个元素。并令next[1]=0;这样如果j==0,我们就知道T的元素到第一个了,应该让j=1,i++;进行下一个元素的匹配。

next[ j ] = k,{ k=Max(t1'.length,t1''.length) }。因为符合条件的t1可能有好多个,所以我们选那个长度最长的t1。

next[ j ]=1。若j之前的那段数组没有符合条件的,则令其为1。下面开始正式说明如何建立next[]

建立next[],其实也和比较两个字符串类似。我们可以设两个相同的T字符串,源字符串T1,匹配字符串T2,在开始时,使T2的第一个元素对应T1的第二个元素(否则没法比较,因为它们都相同).......(Talk is cheap ,show me the code --Linus)

/** *  * @param str 对str字符串建立next数组 * @param next */private static void next(String str,int []next){next[1]=0;/* * i指示T1的位置 * j指示T2的位置 * same表示i之前相同元素的最大个数,即相同小数组的最大长度(不包括i) * 用same为next[]设值 */int i=2,j=1,len=next.length,same=0;char a=0,b=0;while(i<len){a=str.charAt(i-1);b=str.charAt(j-1);//表示在第一次到大该元素时,就为其设置值,因为推R的原因,可能会多次比较,//其值为i之前相同元素的个数+1if(next[i]==0){next[i]=same+1;}//两个元素相同,比较下一个,并令same++if(a==b){same++;i++;j++;}else{//不同向后推T2,设置新的j值,并更改same的值,改为j-1,若存在的话,其值相当于t1。length//不存在,即same<0说明到达第一个元素j=next[j];same=j-1;if(same<0){same=0;}//意味着到达T2的第一个元素,应该都向后移一个元素,并令same为0if(j==0){i++;j=1;same=0;}}}}


这样next[]就设置完成了,下面给出我的KMP算法:

private static int find(String pattern,String compile,int pos){int i=pos,j=1,len_pattern=pattern.length(),len_compile=compile.length();char a=0,b=0;while(i<len_pattern+1&&j<len_compile+1){a=pattern.charAt(i-1);b=compile.charAt(j-1);if(a==b){i++;j++;}else{j=next[j];if(j==0){j=1;i++;}}}if(i<len_pattern){return i-j+1;}else if(j==len_compile+1){return i-j+1;}else{return -1;}}