KMP算法

why

之所以写这篇博客,是因为字符串处理比较常见,字符串处理里面的字符串匹配问题也非常常见
KMP算法的性能在字符串匹配上是最优的
因此在字符串匹配的问题上,可尽量将问题转化为判断一个字符串是否是另一个字符串的子串,再采用KMP算法进行求解

what

KMP算法是一种空间换时间的做法,通过存储公共前缀后缀来快速的进行字符串匹配

假如目标字符串A是 a a a c a a a b d
模式字符串B是    a a a b

BM

如果是采用BM(暴力求解)的方式
会有两个指针i和j,i指向字符串A当前匹配的字符,j指向字符串B当前匹配的字符

因此根据暴力的方式匹配步骤为:   i    j
                            0    0
                            1    1
                            2    2
在匹配两个字符串下标3的字符时,会发现两个字符不相等,就会将字符串B整体向右移,得到

目标字符串A:     a a a c a a a b d
模式字符串B:     a a a b
此时再重新匹配的话指针i从1开始匹配,步骤为
匹配步骤为:   i    j
             0    0
             1    1
在匹配两个字符串下标为2的字符时,会发现两个字符不相等,此时依旧将字符串B整体向右移动、i从2开始匹配

此时的时间复杂度为O(m*n)

KMP

采用KMP算法的话有两个东西必须要先理解,分别时前缀表和next数组
前缀表:

    a         0(其最大公共前缀后缀为0)
    a a       1(其最大公共前缀后缀为1,为a)
    a a a     2(其最大公共前缀后缀为2,为aa)
    a a a b   0(其最大公共前缀后缀为0)

求next,将前缀表中求得的最大公共前缀后缀数置于字符串底下,再整体向右边移动,第一位置-1,得:

        a a a b
  next:-1 0 1 2

操作流程如下:

  1. 假设目标字符串匹配到i,模式字符串匹配到j;

    如果j = -1,或者当前字符匹配成功(A[i] == B[j]),则i++、j++,并且继续匹配下一个字符
    如果j != -1 并且当前字符匹配失败(A[i] != B[j]),则i不变,j = next[j]。

  2. 相当于匹配失败的时候,模式串B相对于A移动了j - next[j]位,也就是说在匹配下标为3失败的时候,字符串会向右挪3 - 2 = 1 位,而指针i依旧保持不变继续匹配
移动前:
  目标字符串A:     a a a c a a a b d
  模式字符串B:     a a a b

移动后:
  目标字符串A:     a a a c a a a b d
  模式字符串B:       a a a b

由于i为3(不变),j为2(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 2 - 1 = 1

移动前:
  目标字符串A:     a a a c a a a b d
  模式字符串B:       a a a b

移动后:
  目标字符串A:     a a a c a a a b d
  模式字符串B:         a a a b

由于i为3(不变),j为1(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 1 - 0 = 1

移动前:
  目标字符串A:     a a a c a a a b d
  模式字符串B:       a a a b

移动后:
  目标字符串A:     a a a c a a a b d
  模式字符串B:           a a a b

由于i为3(不变),j为0(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 0 - (-1) = 1

移动前:
  目标字符串A:     a a a c a a a b d
  模式字符串B:         a a a b

移动后:
  目标字符串A:     a a a c a a a b d
  模式字符串B:             a a a b

由于j为-1,因此i为4(自增1),j为0(也自增1),因此对比后发现匹配上了

此时的时间复杂度为O(m+n)

HOW

算法的实现分为两步,第一是求next数组,第二是求KMP

public class Main {

    public static void main(String[] args) {
        char []test = {'a','b','c','d','a','b'};
        int []result = getNextArray(test);
        for (int i:result){
            System.out.print(i+",");
        }
    }

    public static int[] getNextArray(char[] t){
        int []next = new int[t.length];
        next[0] = -1;
        next[1] = 0;
        int k;
        for (int j = 2; j < t.length; j++){
            k = next[j-1];
            while (k!=-1){
                if (t[j-1] == t[k]){
                    next[j] = k+1;
                    break;
                } else {
                    k = next[k];
                }
                next[j] = 0;
            }
        }
        return next;
    }

    public static int kmpMatch(String s, String t){
        char[] s_arr = s.toCharArray();
        char[] t_arr = t.toCharArray();
        int[] next = getNextArray(t_arr);
        int i = 0, j = 0;
        while (i

你可能感兴趣的:(算法,数据结构,字符串处理)