KMP 算法

作者: XZhongWen | 来源:发表于2018-06-03 15:50 被阅读0次

KMP 算法

1. 暴力匹配算法

在分析KMP算法前, 先看看暴力匹配算法是如何工作的.
暴力匹配算法的基本思想是: 从主串的第pos个字符起和模式串的第一个字符比较, 若相等,则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式串的字符比较. 依次类推, 直至模式串T中的每个字符和主串S中的一个连续的字符序列相等, 则匹配成功, 否则匹配不成功.
例如
主串S: S1 S2 S3 S4 ...... Sn; 模式串T: T1 T2 T3 T4 ...... Tn
当S[i] == T[j]时, 则i++, j++

BF.png

当S[i] != T[j]时, 则i指针回溯到S2处, j指针回到起始位置T1处再进行匹配

BF1.png

假设主串S中有m个字符, 模式串T中有n个字符, 那么暴力匹配算法的时间复杂度可以表示为O(m*n)

小思考

  1. 当主串中S[i] != T[j]时, 主串的i指针是否需要回溯到本次匹配的第一个字符的下一个位置呢?


    Back.png

由上图我们可以看出, i指针回溯的目的是为了让模式串T分别从主串S的S2,S3字符处开始匹配, 避免漏掉模式串T从主串的S2字符处或者S3字符处与主串S产生匹配的情况; 但是我们已知S1S2S3 == T1T2T3, 即 S1 == T1, S2 == T2, S3 == T3, 所以我们只需要拿T1与T2,T3进行比较, 即可判断模式串T是否会在S2或S3处开始于主串匹配, 据此我们可以通过只移动模式串的指针j而不移动主串的i指针来继续进行模式匹配的目的.
结论: 在整个模式匹配的过程中, 主串i指针可以不回溯

KMP 算法

KMP 算法相对于暴力匹配算法的改进在于: 在模式匹配的过程中, 如果出现了失配的情况, 不需要回溯主串的i指针, 而是利用已经得到的"部分匹配"的结果将模式串T向右"滑动"尽可能远的距离,再继续与主串进行比较

  • 下面以主串S, 模式串T为例, 分析当S与T失配时, 模式串T的j指针应该如何移动?


    Screen Shot 2018-05-19 at 3.16.07 PM.png

此时S[i] != T[j]
那么假设此时模式串T的指针j需要回溯到k位置再与主串S进行比较, 如图


Screen Shot 2018-05-19 at 3.38.26 PM.png

这里的k需要满足的条件是: T[0]T[1]...T[k-1] == T[j-k+1]...T[j-1]
从这里我们可以看出, 模式串T中k位置位主串S是无关的, 它是通过模式串本身的特性确定的; 由此我们在进行模式匹配前, 可以先对模式串T的各个位计算其k值; 当模式串T与主串S失配时, 我们就可以直接根据模式串T的失配位获取对应的k值, 然后让模式串T的j指针回溯到k位置再与主串S进行比较

  • 如何确定模式串T各个位的k值?
这里我们先确定k值的含义

k指的是当模式串中指针j所指向的字符与主串中指针i所指向的字符失配时, 在模式串T需要重新和主串S中i指针指向的字符进行比较的字符的位置
设next(j) = k, 则模式串存在下列关系:
T[0]T[1]...T[k-1] == T[j-k+1]...T[j-1]
其中k为满足1<k<j的某个值, 此时next(j + 1) = ?可能有两种情况:
(1) T[k] = T[j]
表明在模式串中T[0]T[1]...T[k] == T[j-k+1]...T[j], 则next(j + 1) = next(j) + 1
(2) T[k] != T[j]
表明在模式串中T[0]T[1]...T[k] != T[j-k+1]...T[j], 此时可以把求next的函数值的问题看成是一个模式匹配的问题, 整个模式串既是主串也是模式串, 而当前的匹配过程中, 已有T[0]T[1]...T[k] == T[j-k+1]...T[j], T[k] != T[j], 现将模式右滑至以模式中的第next(k)个字符和主串中的第j个字符比较, 如果next(k) = k', 且T[j] = T[k'], 则说明在主串中第j+1个字符前存在一个长度为k'的最长子串和模式串中从首字符起长度为k'的子串相等,即T[0]T[1]...T[k'] == T[j-k'+1]...T[j], 此时next(j + 1) = next(k) + 1; 同理如果T[j] != T[k'], 则继续进行上述过程直至T[j]和模式中某个字符匹配成功或者不存在任何k'满足条件,则next(j + 1) = 1.

总结

  1. 当j = 1时, next(j) = 0; 此时主串i指针右移一位, 即i++; 模式串从主串的第i位开始匹配
  2. 当j = 0时, next(j) = 1; 此时模式串j指针回溯到第一位再与主串第i位进行匹配
  3. 当j = Max{k|1 < k < j, 且T[0]T[1]...T[k-1] == T[j-k+1]...T[j-1]}时, 模式串指针j回溯到next(j)处再与主串第i位进行匹配

KMP算法实现

//
//  main.c
//  KMP
//
//  Created by 肖仲文 on 2018/6/3.
//  Copyright © 2018 肖仲文. All rights reserved.
//

#include <stdio.h>

#define MAXSTRLEN 255
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度

/**
 生成一个值为chars的串

 @param T 串
 @param chars 字符串常量
 */
void strAssign(SString T, const char *chars) {
    
    if (chars == NULL) {
        return;
    }
    
    int index = 0;
    while (*(chars + index) != '\0') {
        T[index + 1] = *(chars + index);
        index++;
    }
    T[0] = index;
}

/**
 求模式串T的next函数值并保存到数组next

 @param T 模式串
 @param next next值数组
 */
void get_next(SString T, int next[]) {
    int i = 1;
    int j = 0;
    next[i] = j;
    while (i < T[0]) {
        if (j == 0 || T[i] == T[j]) {
            i++;
            j++;
            next[i] = j;
        } else {
            j = next[j];
        }
    }
}

/**
 KMP算法

 @param S 主串
 @param T 模式串
 @param next 模式串的next值数组
 @return 模式串与主串匹配时的位置, 不匹配返回0
 */
int index_kmp (SString S, SString T, int next[]) {
    int i = 1;
    int j = 1;
    while (i <= S[0] && j <= T[0]) {
        if (j == 0 || S[i] == T[j]) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }
    if (j > T[0]) {
        return i - T[0];
    } else {
        return 0;
    }
}

int main(int argc, const char * argv[]) {
    
    // 定义主串和模式串
    SString mainStr, patternStr;
    
    // 初始化主串和模式串
    strAssign(mainStr, "ababcabcacbab");
    strAssign(patternStr, "abcac");
    
    // next值
    int next[patternStr[0] + 1];
    get_next(patternStr, next);
    
    // kmp
    int pos = index_kmp(mainStr, patternStr, next);
    printf("pos = %d\n", pos);
    
    return 0;
}

相关文章

  • KMP 专题整理

    KMP 学习记录 kuangbin专题十六——KMP KMP 学习总结 朴素 KMP 算法 拓展 KMP 算法(E...

  • 对KMP算法的一些理解

    最近学到KMP算法,下面讲讲对KMP算法的一些个人理解,希望对大家有帮助! 对于KMP算法的理解: 整个KMP算法...

  • KMP算法文章合集

    字符串的查找:朴素查找算法和KMP算法 暴力匹配算法与KMP算法(串的匹配) 字符串查找算法BF和KMP 字符串匹...

  • 串的模式匹配算法

    KMP算法 算法匹配

  • 问答|KMP算法学习笔记

    问题 目录KMP是什么,做什么用的KMP算法的高效体现在哪如何KMP算法的next数组KMP的代码KMP的时间复杂...

  • KMP算法——寻找子串位置

    KMP算法——寻找子串位置 1、KMP算法简介: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J....

  • 字符串匹配 - KMP算法

    前面我们介绍非常高效的 BM 算法,今天我们介绍另一个非常出名且高效的 KMP 算法。 KMP 算法思想 KMP ...

  • KMP算法及优化

    转载请注明出处: KMP算法及优化 今天看到同学在复习数据结构书上的KMP算法,忽然发觉自己又把KMP算法忘掉了,...

  • KMP算法(字符串匹配问题)

    一、是什么? 注意,是KMP算法,不是MMP哈,我没有骂人。KMP算法是用来做字符串匹配的,除了KMP算法分,还有...

  • KMP算法

    KMP算法

网友评论

    本文标题:KMP 算法

    本文链接:https://www.haomeiwen.com/subject/ypsodftx.html