字符串匹配--KMP算法

字符串匹配指在主串字符串s,中找到模式串t。

字符串匹配指在主串字符串s,中找到模式串t。

BF算法

BF算法是朴素的字符匹配算法,思想是:如果模式串t和主串s,在对应位置匹配那么下标同时增加1,否则模式串从头开始,回到上次匹配中主串开始位置的下一位置继续往后比较,直到主串末尾。可以看到其时间复杂度为O(m*n)。

KMP算法

kmp在bf算法上进行了改进,主要是在模式串每次都要从头开始与主串匹配这一过程进行了改进。

假设t从0到j-1 与 s从i-j到i-1匹配,在j和i出处失配,见下图

字符串匹配--KMP算法_第1张图片

如果t串的T0-Tj-1 不等于 T1-Tj(红色线所示) 那么

t串回溯时按照bf算法,回溯到S(i-j+1)处,T(0)-T(j) (细红线所示)必然不会与 S(i-j+1)到S(i)匹配,因为红叉部分与S的绿色部分匹配了,这样造成了不必要的回溯。

同理,回溯到S(i-j+2)如果满足 t串的T0-T(j-2) 不等于 T2-Tj,t和s也是失配的

那么只有找到一个k 值满足 T(0)-T(k-1) 等于 T(j-k)-T(j-1)时说明T串可以直接回溯到k位置,此时满足S(i-k)-S(i-1) 等于T(0)-T(k-1),可以直接从T(k)处和S(i)进行匹配。绿色线部分,所以减少了不必要的回溯,提高效率。

字符串匹配--KMP算法_第2张图片

kmp算法的思想:

s和t,下标从都从0开始,字符匹配则i,j都加1,否则,j就回溯到上面提到的k处,i不变。继续比较,匹配i+1,j+1,否则j继续重复回溯到此时j对应的k处,不断重复。。。。。直到,(1).j回溯时的k与Si匹配则i,j增加1,(2)j回退到j==-1,此时i,j增加1,S(i+1)和T0比较。

那么,重要的就是如何求得k的值了,k的值只跟模式串自身有关,我们将S(j)对应的k存储在next(j)数组中,

k满足 模式串t 中 k之前 k个字符 与 T(j)之前的 k个字符相等。j是与主串在i处失配的位置这也得到,i之前k个字符与t的k位置之前k个字符相等。

下面是java的kmp算法实现。

//获取next[]数组
public static int[] getNext(String pattern) {
        int j = 0;// pattern 的j位置
        int k = -1;// k的初值
        int[] next = new int[pattern.length()];
        next[0] = -1;// j==0 时 k ==-1

        while (j < pattern.length() - 1) {

            if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {
                j++;
                k++;
                next[j] = k;
            } else {
                k = next[k];// k 取上次的k值
            }
            System.out.println("j=" + j + ",k=" + next[j]);
        }
        return next;

    }
//kmp算法求匹配的下标

public static int KMPIndex(String s, String t) {

        int i = 0, j = 0;
        int[] next = getNext(t);

        while (i < s.length() && j < t.length()) {
            if (j == -1 || s.charAt(i) == t.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];// i 不变,j回溯到k
            }
        }

        if (j >= t.length()) {
            return i - t.length(); // s中匹配到了t
        } else {
            return -1;// 未匹配到 t
        }
    }
//测试

public class KMP {
    public static void main(String[] args) {
        String s = "ababcabcacbab";
        String t = "abcac";

        System.out.println("匹配到t的位置为:" + KMPIndex(s, t));
    }

    public static int[] getNext(String pattern) {
        int j = 0;// pattern 的j位置
        int k = -1;// k的初值
        int[] next = new int[pattern.length()];
        next[0] = -1;// j==0 时 k ==-1

        while (j < pattern.length() - 1) {
                if (k == -1 || pattern.charAt(j)   ==pattern.charAt(k)) {
                j++;
                k++;
                next[j] = k;
            } else {
                k = next[k];// k 取上次的k值
            }
            System.out.println("j=" + j + ",k=" + next[j]);
        }
        return next;

    }

    public static int KMPIndex(String s, String t) {

        int i = 0, j = 0;
        int[] next = getNext(t);

        while (i < s.length() && j < t.length()) {
            if (j == -1 || s.charAt(i) == t.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];// i 不变,j回溯到k
            }
        }

        if (j >= t.length()) {
            return i - t.length(); // s中匹配到了t
        } else {
            return -1;// 未匹配到 t
        }
    }
}

结果

j=1,k=0
j=1,k=0
j=2,k=0
j=2,k=0
j=3,k=0
j=4,k=1
匹配到t的位置为:5

总结:kmp对bf算法进行了改进,主要是有模式串求出j对应的next数组,kmp算法的时间复杂度为O(m+n)。

你可能感兴趣的:(数据结构与算法)