KMP字符串匹配算法过程原理

KMP算法是无回溯的字符串匹配算法

此算法的关键是求next(j);
若设在进行某一趟匹配比较时在模式P的第j位失配,

  • 如果j>0,那么在下一趟比较时模式串P的起始比较位置是next[j],目标串T的指针不回溯,仍指向上一趟失配的字符;
  • 如果j=0,则目标串指针T进一,模式串指针P回到 p0 p 0 ,继续进行下一趟匹配比较。

知道了next数组怎么使用后,再介绍KMP算法的原理。
朴素模式匹配速度慢的原因是有回溯,而这些回溯是可以避免的。例如:
T a b b a b a
P a b a
第一趟比较,t0 = p0,t1=p1,t2 p2,但p0 p1,由此可推知t1(=p1) p0,将P右移一位,用t1和p0比较肯定不等,这一趟可以不比较。又由于p0=p2,所以t2 p0 (p0=p2),将P再右移一位,用t2和p0比较也不会相等。我们应该将P直接右移3位。从t3和p0开始进行比较。这样的匹配过程对于T来说就消除了回溯。

一般情形

如果有从目标T的第s个位置 ts t s 与模式P的第0个位置 p0 p 0 开始进行比较,直到在目标T第s+j位置 ts+j t s + j ”失配”,这时应有:

tsts+1ts+2...ts+j1=p0p1p2...pj1 t s t s + 1 t s + 2 . . . t s + j − 1 = p 0 p 1 p 2 . . . p j − 1

按朴素模式匹配算法,下一趟应从目标T的第s+1个位置起用 ts+1 t s + 1 于模式P中的 p0 p 0 对其,重新开始匹配比较。若想匹配,必须满足:

ts+1ts+2ts+3...ts+j...ts+m=p0p1p2...pj1...pm1 t s + 1 t s + 2 t s + 3 . . . t s + j . . . t s + m = p 0 p 1 p 2 . . . p j − 1 . . . p m − 1

如果在模式串P中,

p0p1p2...pj2p1p2...pj1 p 0 p 1 p 2 . . . p j − 2 ≠ p 1 p 2 . . . p j − 1

则第s+1趟不用进行匹配比较,就能断定它必然“失配”。
依此类推,直到对于某一个值k,使得:

p0p1p2...pk+1pjk2pjk1...pj1 p 0 p 1 p 2 . . . p k + 1 ≠ p j − k − 2 p j − k − 1 . . . p j − 1

p0p1p2...pk=pjk1pjk...pj1 p 0 p 1 p 2 . . . p k = p j − k − 1 p j − k . . . p j − 1

才有 p0p1p2...pk=ts+jk1ts+jk...ts+j1 p 0 p 1 p 2 . . . p k = t s + j − k − 1 t s + j − k . . . t s + j − 1

这样,可以把在某趟比较“失配”时的模式P从当前位置直接向右滑动 j-k-1位。
可以从T中的 ts+1 t s + 1 (即上一趟失配的位置)与模式串的 pk+1 p k + 1 开始,继续向下进行比较。

next特征函数

用一个next特征函数来确定:当模式P中的第j个字符与目标串T中相应的字符失配时,模式P中应当由哪个字符(设为第k+1个)与目标中刚失配的字符重新继续进行比较。

  • 计算next(j)的算法
public static int[] next(String pat) {
        int[] next = new int[pat.length()];
        int j = 0, k = -1;
        next[0] = -1;
        while (j < pat.length()-1) {
            if (k == -1 || pat.charAt(j)==pat.charAt(k)) {
                j++;
                k++;
                next[j] = k;    
            }else {
                k = next[k];
            }
        }   
        return next;
    }
  • KMP算法
public static int  KMPMatch(String tar,String pat) {
        int[] next= new int[pat.length()];
        int p = 0;
        int t = 0;
        int plen = pat.length();
        int tlen = tar.length();
        next = next(pat);
        while (p < plen && t < tlen) {
            if (p == -1 || pat.charAt(p) == tar.charAt(t)) {
                p++;
                t++;
            }
            else  p = next[p];
        }
        if (p < plen) return -1;
        else return t-plen;
    }
public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String tar = sc.nextLine();
        String pat = sc.nextLine(); 
        System.out.println("KMP匹配结果:"+KMPMatch(tar, pat));
    }
  • 运行结果
acabaabaabcacaabc
abaabcac
KMP匹配结果:5

next数组为:
-1 0 0 1 1 2 0 1

总结:KMP为无回溯的算法,在计算next过程中,实际上也是一个模式匹配的过程,只是目标串和模式串现在是同一个串P,也是无回溯的过程。KMP算法的时间复杂度为O(lengthT+lengthP)。

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/ningchunmei1/article/details/79759610
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞