kmp算法是典型的字符串匹配算法。
相对于朴素字符串匹配算法来说,由于其在计算时进行了预处理,
所以在时间复杂度上比朴素字符串匹配算法要高。
其核心在于kmp算法在计算时的预处理。
预处理是干什么呢?
这里我们先给出一个算法导论上面字符串及其预处理结果。
字符串str为ababababca
预处理结果数组为:
[0, 0, 1, 2, 3, 4, 5, 6, 0, 1]
预处理结果数组与字符串的长度是一样的。
对于预处理结果数组result来说,
result[i]=max{x<i,并且str从开始到x的字串是str从开始到i的子串的后缀}
这个可能有点难懂,我们结合实例来分析下,
很明显,
result[0]=0;
看result[1],这里str从开始到i的子串为ab
str从开始到0的子串为a,a不是ab的后缀,所以result[1]=0;
然后看result[2],这里str从开始到i的子串为aba,
str从开始到0的子串为a,a是aba的后缀,str从开始到1的子串为ab,ab不是aba的后缀,
所以result[2]=1;
然后是result[3],这里str从开始到i的子串为abab,
str从开始到0的子串为a,a不是abab的后缀,str从开始到1的子串为ab,ab是abab的后缀,str从开始到2的子串为aba,aba不是abab的后缀,
所以result[3]=2,依次类推。
匹配的时候呢,
假设我们来匹配abababac与aba
就是在abababac中查找aba在何位置出现,
那么,先查找它们的第一位,第二位,第三位,发现都匹配上了,
这个时候,输出第一个结果,但是继续查找的时候与朴素字符串匹配不同。
朴素字符串这个时候从abababac的第二位,aba的第二位开始继续搜索,
但是我们有了预处理的结果,不从aba的第一位开始搜索了,
而是从aba的第二位,abababac的第四位继续搜索。
所以时间复杂度有了提高。
下面是简单的测试程序:
/** * 在target字符串中寻找source的匹配 * @param target * @param source */ public void kmp(String target, String source) { int sourceLength = source.length(); int targetLength = target.length(); int[] result = preProcess(source); int j = 0; int k = 0; for(int i=0;i<targetLength;i++){ //找到匹配的字符时才执行 while(j>0 && source.charAt(j) != target.charAt(i)){ //设置为source中合适的位置 j = result[j-1]; } //找到一个匹配的字符 if(source.charAt(j) == target.charAt(i)){ j++; } //匹配到一个,输出结果 if(j == sourceLength){ j = result[j-1]; k++; System.out.println(“find”); } } } /** * 预处理 * @param s * @return */ public int[] preProcess(final String s) { int size = s.length(); int[] result = new int[size]; result[0] = 0; int j = 0; //循环计算 for(int i=1;i<size;i++){ while(j>0 && s.charAt(j) != s.charAt(i)){ j = result[j]; } if(s.charAt(j) == s.charAt(i)){ j++; } //找到一个结果 result[i] = j; } System.out.println(java.util.Arrays.toString(result)); return result; } public static void main(String[]args) throws Exception { final String s = “abcdabcd”; KMP k = new KMP(); k.kmp(s, “abcd”); }
下面是其的一个简单应用:
poj2406链接见:
http://poj.org/problem?id=2406
计算这个字符串被重复了多少次
这里面是kmp预处理算法的应用,
对输入字符串进行预处理,
得到最后一个字符的对应数组值,简单计算后就可以得到结果,
ac程序如下:
import java.util.Scanner; public class Problem_2406 { public static void main(String []args) throws Exception { Problem_2406 p = new Problem_2406(); Scanner scanner = new Scanner(System.in); String s = “”; while(true) { s = scanner.nextLine(); if(s.equals(“.”)) { break; } System.out.println(p.kmp(s)); } } public int kmp(String input) { int len = input.length(); int[] next = new int[len + 1]; int i = 0,j = -1; next[0] = -1; while(i < len){ if( j == -1 || input.charAt(i) == input.charAt(j)){ i++; j++; next[i] = j; }else{ j = next[j]; } } if(len % (len – next[len]) == 0){ return len /(len – next[len]); } return 1; } }