在正式开始之前先了解一些预备知识
- 前缀子串:模式串p开头的前k个字符,例如 p(0),p(1),p(2)...p(k-1);
- i位置的后缀字串:在P第i位置的左边,取出k个字符(包括i位置),即p(i-k+1),…,p(i)
-
- i位置的后缀字串:在P第i位置的左边,取出k个字符(包括i位置),即p(i-k+1),…,p(i)
- 特征数:第i位的最长前缀串的长度k就是模式串P在位置i上的特征数n[i]。
- 特征数组成的向量称为该模式串的特征向量。
其意义在于:
表示一旦匹配过程中pi与tj比较不等,可用p中以n [i-1]为下标的字符与tj进行比较。
特征数的定义
- n[0]=0,对于i>0的n[i],假定一直前一位置特征数n[i-1]=k;
- 如果i>0且q[i]=q[k](q表示模式串字符数组),则n[i]=k+1;
- 当q[i]!=q[k]且k!=0时,令k=n[k-1],重复执行第三步,直至条件不满足;
- 当q[i]!=q[k]且k=0时,则n[i]=0。
例子:假定模式串p="abaabcac",则对应的特征值分别是n={0 0 1 1 2 0 1 0 };
真正的KMP算法包含两部分,一个是计算模式串的特征向量,另一部分是进行匹配,下面是代码实现
import java.util.*;
public class KMP_suanfa {
/**
* @param args
*/
public static ArrayList
beforeMatch(String p){
int plen=p.length();
char[]c=p.toCharArray();
ArrayList
list=new ArrayList
();
list.add(0);
for(int i=1;i
0&&c[i]!=c[k]){
k=list.get(k-1);
}
if(c[k]==c[i]){
list.add(k+1);
}else{
list.add(0);
}
}
return list;
}
/**
*
* @param target 目标字符串
* @param pstr 模式串
* @param list 模式串下特征值
* @param start 匹配开始位置
* @return
*/
public static int KMP_match(String target,String pstr,ArrayList
list,int start){ int last=target.length()-pstr.length(); if(last-start<0){ return -1; } char[]t=target.toCharArray(); char[]p=pstr.toCharArray(); int j=0; for(int i=start;i
0){ j=list.get(j-1); } if(t[i]==p[j]){ j++; } if(j==p.length){ return i-j+1; } } return -1; } public static void main(String[] args) { // TODO Auto-generated method stub ArrayList
list=beforeMatch("abaabcac"); for(int i:list){ System.out.print(i+" "); } System.out.println(""); String target="acabaabaabcacaabc"; System.out.println(KMP_match(target, "abaabcac", list, 0)); } }