KMP算法代码

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时,减少不必要的操作。

 

posted @ 2017-02-07 12:26  mahuan2  阅读(322)  评论(0编辑  收藏  举报