zoukankan      html  css  js  c++  java
  • KMP算法

    假如我们需要在一段很长的文本中查找一个单词出现的位置,你会怎么做?朴素的做法是从文本的字母开始,向后和这个单词中的字母一一匹配,若完全匹配则说明找到了一个单词,若有一个字母匹配失败,就紧接着换文本的下一个字母重新匹配,即使成功匹配到一个单词,也要紧接着从下一个字母开始匹配。显然,最坏情况复杂度可以达到O(nm)(n为文本长度,m为要查找的单词长度)。事实上,那段文本我们称为文本串(T),那个单词称为模式串(P),这个问题叫做字符串匹配。

    同样的结果,KMP算法却可以做到O(n+m),因为他避免了许多不必要的比较,比如abababc为文本串,ababc为模式串,一开始我们从头开始匹配,当模式串匹配到c时失配,朴素做法会重新从模式串开头匹配,而KMP算法却不会。为什么呢?只是一个字母失配,在此之前我们已经成功匹配了一些,难道没有用处吗?可能当年KMP算法的提出者也是这样想的。我们可以利用已匹配的字母,设fail数组保存某个字母的前缀(不包括该字母)开头和结尾最长的公共部分长度,一旦失配,我们就可以以此跳到一个合适的位置再去匹配,比如上面的例子,KMP算法就会再从模式串的第三个字母a开始匹配。还有,fail数组可以保存不同的信息,我习惯这样。

    下面简述一下如何得到fail数组,我们假设已经得到了fail[i],那么可以去推出fail[i+1],若P[i]==P[fail[i]],fail[i+1]=fail[i]+1;否则,说明fail[i+1]为0。最开始就有fail[0]=fail[1]=0。

    得到了fail数组,去匹配文本串也很容易,过程十分相似。我们枚举文本串每个字母Ti,设j表示在模式串中已匹配的长度,若P[j]==T[i],则++j;否则j=fail[j]。当j==m时,说明成功匹配到了一个。继续进行上述过程就可以找出所有模式串的匹配。

    仔细想想,可以知道,代码这样做,字符串下标是从0开始的。

     1 int n, m, fail[maxn]; //n和m是t和p的长度
     2 char t[maxn], p[maxn];
     3 void getfail() {
     4     fail[0] = fail[1] = 0;
     5     for (int i = 1; i < m - 1; ++i) {
     6         int j = fail[i];
     7         while (j && p[j] != p[i]) j = fail[j];
     8         fail[i + 1] = p[j] == p[i] ? j + 1 : 0;
     9     }
    10 }
    11 void kmp() {
    12     getfail();
    13     int j = 0;
    14     for (int i = 0; i < n; ++i) {
    15         while (j && p[j] != t[i]) j = fail[j];
    16         if (p[j] == t[i]) ++j;
    17         if (j == m) printf("%d
    ", i - m + 1);
    18     }
    19 }
  • 相关阅读:
    python--Time(时间)模块
    python基础:冒泡和选择排序算法实现
    浅谈python的深浅拷贝
    python随笔--根据号码查询归属地
    python处理字符串:将字符串中的数字相加求和
    Wi-Fi 6解释:下一代Wi-Fi
    Wifi5和Wifi6的区别
    VS Code配置Git环境 X64
    VS Code配置C/C++环境 X64
    MikroTik CCR1036与Tilera GX36处理器
  • 原文地址:https://www.cnblogs.com/Mr94Kevin/p/9656069.html
Copyright © 2011-2022 走看看