public class KMP { public static void main(String[] args) { KMP kmp = new KMP(); Integer[] match = kmp.match("aacaabaabaac", "aabaa"); System.out.println(Arrays.toString(match)); } public int[] getMatchLength(String pattern) { int matchLength = 0; // maxLength表示长度为i的字符串前缀和后缀的最长公共部分,i从1开始,maxLength[0]和maxLength[1]都为0 int[] maxLength = new int[pattern.length() + 1]; // index+1是当前得字符串长度,寻找字符串的前缀和后缀公共部分的最大长度 for (int index = 1; index < pattern.length(); index++) { // 如果index位置的字符和matchLength位置的字符不相同,matchLength退化为长度为matchLength的字符串的最大公共长度 while (matchLength > 0 && pattern.charAt(matchLength) != pattern.charAt(index)) matchLength = maxLength[matchLength]; // 如果index位置的字符和matchLength位置的字符相同,matchLength长度加1,否则matchLength会为0 // 初始状态以及上面的公共序列长度发现过程,会保证matchLength为0 if (pattern.charAt(matchLength) == pattern.charAt(index)) matchLength++; // index+1长度的字符串,前缀和后缀公共部分的最大长度未matchLength maxLength[index + 1] = matchLength; } return maxLength; } public Integer[] match(String source, String pattern) { int[] matchLength = getMatchLength(pattern); int sourceIndex = 0, patternIndex = 0; List<Integer> matchIndex = new LinkedList<Integer>(); for (; sourceIndex < source.length(); sourceIndex++) { // 如果sourceIndex和patternIndex位置的字符不相同,patternIndex退化为,pattern中从起始位置开始、长度为patternIndex的字符串的最大公共长度,直到为0 while (patternIndex > 0 && source.charAt(sourceIndex) != pattern.charAt(patternIndex)) { patternIndex = matchLength[patternIndex]; } // 如果soruceIndex位置和patternIndex位置的字符相等,patternIndex增加1 if (patternIndex < pattern.length() && source.charAt(sourceIndex) == pattern.charAt(patternIndex)) { patternIndex++; } // 如果patternIndex为pattern的长度,则发现一个匹配,保存sourceIndex的位置信息,同时patternIndex退化为,pattern中从起始位置开始、长度为patternIndex的字符串的最大公共长度 if (patternIndex == pattern.length()) { matchIndex.add(sourceIndex - pattern.length() + 1); patternIndex = matchLength[patternIndex]; } } Integer[] arrays = new Integer[0]; return matchIndex.toArray(arrays); } }
通过对pattern中,从起始位置开始,长度为i的字符串,进行前缀、后缀最大公共长度进行计算,可在后续的字符串匹配时减少不必要的操作。、
设:原始字符串为srouce,模式字符串为pattern
使用数组matchLength保存最大公共长度值,matchLength的长度为pattern.length()+1。如果matchLength[10]为3,表示pattern[0:10-1]的最大公共长度为3。
在计算前后缀的最大公共长度时,发现长度为a的字符串的最大公共长度为matchLength[a],设为x,即pattern[0:a-1]中,pattern[0:x-1]==pattern[a-x:a-1]。
计算长度为a+1的字符串的最大公共长度时,可启发式的基于长度为a的字符串的计算结果,如果pattern[(x-1)+1]==pattern[(a-1)+1],则长度为a+1的字符串的最大公共长度为x+1。
否则继续基于长度为x(matchLength[a])的字符串的计算结果,进行计算,直到x为0。长度为x的字符串的最大公共长度,为matchLength[x]。
通过以上方式可以计算出matchLength的各项数值,并在source中匹配pattern时,减少不必要的操作。