学了一天终于了解KMP算法了,下面我通过图片讲解一下。
先说一下Bru-Force算法,比如我们要从序列:
1 2 3 1 2 3 1 2 3 1 2 3 4中寻找序列1 2 3 1 2 3 4我们可以进行如下操作:
。。。。。。
就这样一步一步可以类推出来子序列在目标串中的位置,但是这种效率如果碰到特殊情况如:目标串为:aaaaaaaaab,目标串:aaab的情况,算法的复杂度就会落到O(n*m),其中n*m分别为目标串和子序列的长度。
而KMP算法可以对这个问题进行优化,下面我们解释KMP算法。
说到KMP算法,首先要说的就是NEXT数组,我们先求出子序列 1 2 3 1 2 3 4的NEXT数组:
我们先不说这个表是怎么来的,我们先看看这东西怎么用,一会再说这个东西怎么求。
先看第一次遍历:
我们发现这个4这个的位置和1不相等,然后我们看一下,4的next数组对应值是3。然后我们就可以直接用子序列下标为3的地方和目标串与子序列不相等那个1比较(注意一点就是如果next数组是-1的话,那子序列从头开始比较,目标串比较位置右移一位),就是打星星的地方:
发现依然是这个4和那个1不相等,方法同上:
然后我们就能求得这个问题的最终结果,这样打打减少了比较的次数。
那么这个目标串什么求呢?
要弄懂这个先要清楚前后缀。
我还以1 2 3 1 2 3 4为例子,
其中1没有前后缀。
1 2:前缀:{1};后缀:{2}。
1 2 3:前缀{1,1 2};后缀{3, 2 3}。
1 2 3 1:前缀{1,1 2,1 2 3};后缀{1,3 1;2 3 1}。
1 2 3 1 2:前缀{1,1 2,1 2 3,1 2 3 1};后缀:{2,1 2,3 1 2,2 3 1 2}
.............
应该很显而易见。
然后假定pattern[i]中最大相等缀值为pattern[0]-pattern[i]中最长的,前后缀相等的长度。
然后next值的公式大概就是:
就以1 2 3 1 2 3 4为例子。
next[0]: 其中pattern[0]=1=pattern[0];所以next[0]=-1。
next[1]: pattern[1]=2, next[1]=pattern[0]的相等缀值,而pattern[0]没有相等缀值,也就是0,所以next[1]=0。
next[2]: 参考next[1]
next[3]: 参考next[1]。其中pattern[3]=1=pattern[0];所以next[3]=-1。
next[4]: 先看pattern[4]的序列为1 2 3 1 2具有前后相同缀{1 2},所以next[4]=0。
next[5]: 参考next[4]
next[6]: 我们看4前面的序列为1 2 3 1 2 3。显而易见可得pattern[5]的相等前后缀都是{1,2,3},缀值为3,所以推出next[6]=4。
最后我们就可以的出来子序列1 2 3 1 2 3 4的next数组为:-1 0 0 -1 0 0 3。
下面贴代码。(JAVA)
计算next数组:
public static int[] getNext(int[]pattern){ //返回值即为next数组
int next[]=new int[pattern.length]; //储存位置。
int j=0; //遍历到的位置
int k=-1; //到目前位置相同缀的长度
next[0]=-1;
while(j<pattern.length-1){
if(k==-1||pattern[j]==pattern[k]){
j++;
k++;
if(pattern[j]==pattern[k]) //如果这个值和前面的匹配到了,那这个next值归0
next[j]=0;
else
next[j]=k;
}
else //如果相等缀值不为-1并且这个点和第一个点的值不一样,相等缀值为-1
k=-1;
}
return next;
}
计算insertOf查找结果代码:
public static int indexOf(int target[],int pattern[],int begin){
int n=target.length,m=pattern.length;
int i=begin,j=0; //i储存目标串遍历位置,j储存子序列遍历位置
int[]next=getNext(pattern); //获取next数组
while(j<pattern.length){
if(j==-1||target[i]==pattern[j]){
i++; //如果读取到-1或者二者匹配值,那目标串和子序列都移一位
j++;
}
else{ //否则如果匹配不相等,子序列移位next
j=next[j];
if(n-i<m-j) //如果目标序列长度不够,终止循环
break;
}
}
if(j==m)return i-j-1;
return -1;
}