• KMP字符串匹配算法详解


      KMP算法利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。

    Next()函数的详解

    • 把将要进行next计算的字符串S分成 k ,j 前后两串,k代表前串开头所在的序号,j代表后串开头所在的序号,起始的时候j=1,k=0。
    • 我们比较一下前串 后串是否相等,要怎么比较呢,肯定是比较S[j]==S[k],如果相等,那么next[j+1]=k+1,然后j++,k++。关键就是理解这个next[j+1]=k+1(为什么k+1?):简单说就是S串中的第j+1个字符的next函数值由他前面的字符与前串相等的个数来决定,就是说串中的第j+1个字符的next函数值,是由他前面的字符串决定的。
    • 当S[j]!=S[k],即不相等的时侯,那么j不动,k返回到开头(因该是next[k]位置,便于理解先假设是返回k=0处),即从头比较S[0]与S[j],S[1]与S[j+1]。

      例如:第 j+1 个字符的next函数值next[j+1]等于3,意味着它的前三个字符串,S[j-2]S[j-1]S[j] =S[0]S[1]S[2]。

    例一:模式串:abcaabcba

    下标

    0

    1

    2

    3

    4

    5

    6

    7

    8

    模式串

    a

    b

    c

    a

    a

    b

    c

    b

    a

    next值

    -1

    0

    0

    0

    1

    1

    2

    3

    0

    1.第一个字符的next值令为-1。令第二个字符b的next值为0。初始k=0,j=1。开始比较S[k] 和S[j]。

    2.比较S[0] !=S[1],所以j++,k不变,next[j=2]=k=0。

    3.比较S[0] !=S[2],所以j++,k不变,next[j=3]=k=0。

    4.比较S[0]==S[3],所以j++,k++,next[j=4]=k=1。

    5.k=1了,所以比较S[1] !=S[4],k返回到next[k]位置,即k=next[1]=0,然后比较S[k=0] == S[4],所以 j++,k++,next[j=5]=k=1。

    6.比较S[1]==S[5],所以j++,k++,next[6]=k=2。

    7.比较S[2]==S[6],所以j++,k++,next[7]=k=3。

    8.比较S[3] !=S[7],所以k返回到next[k=3]位置,即k=next[3]=0,然后比较S[k=0] !=S[7],所以j++,k=0不变,next[8]=k=0。

     

    在例一中,每次不相等时返回的都是k=next[k]=0,都是返回到了开头,下面一个不是返回到开头0的情况:

    例二:模式串:aabcaaabaac

    下标

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    模式串

    a

    a

    b

    c

    a

    a

    a

    b

    a

    a

    c

    next值

    -1

    0

    1

    0

    0

    1

    2

    2

    3

    1

    2

    从 j=5,k=1的时候开始

    5.比较S[1]==S[5],所以j++,k++,next[j=6]=k=2。

    6.比较S[2] !=S[6],所以k返回到next[k=2]位置,即k=next[2]=1,然后比较S[k=1]==S[6],所以 j++,k++,next[7]=k=2。

    ……

    因此,发现K的退回是退回到next[k]的位置,即S[j]!=S[k]时,k=next[k]。

    KMP的算法思想

    和BF算法相比,KMP算法主要是在模式串上下功夫,通过先求得模式串对应的next[ ]数组,当两个字符串中字符匹配失败时候将模式串的下标回溯到next[ ]中存储的下标位置,而BF算法是直接回溯到模式串的0下标,即开始第一个字符。所以KMP算法的时间复杂度要比BF算法好。

    KMP算法代码

     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 char* s = "aabcaaabaac";
     5 char* t = "aac";
     6 
     7 int next[100];  //定义next数组 
     8 
     9 void getNext(char *s, int next[])
    10 {
    11     int k=-1;   / /k代表前串起始位置
    12     int j=0;    //后串起始位置,一直增加
    13     next[0] = -1;   //令第一个字符的next值为-1
    14     
    15     while(j < strlen(s) - 1)    //当后串小于最大下标-1 
    16     {
    17         if(k == -1 || s[j] == s[k]) //匹配的情况下,即s[j]==s[k],next[j+1]=k+1;
    18         {
    19             ++j;
    20             ++k;
    21             next[j] = k;
    22         }
    23         else                   //若不匹配,即p[j]!=p[k],k=next[k]
    24             k = next[k];
    25     }
    26 }
    27 
    28 int KMP(char* s, char* t)
    29 {
    30     int i = 0;  //i从s串开始 
    31     int j = 0;  //j从t串开始 
    32     int sLength = strlen(s);    //s串的长度 
    33     int tLength = strlen(t);    //t串的长度 
    34     while((i < sLength) && (j < tLength))   //当下标i和j都不越界时
    35     {
    36         if(j == -1 || s[i] == t[j])     //当模式串t中第一个字符与目标串s中某个字符匹配失败时,i应该移动到目标串s的下一个目标,再和模式串t的第一个字符进行比较,或者s的第i个字符和t的第j个字符相等,则将i++和j++ 
    37         {
    38             i++;
    39             j++;
    40         }
    41         else
    42         {
    43             //i=i-j+1;j=0;  //这是普通的BF算法,将模式串的下标从0开始 
    44             j = next[j];    //KMP算法是将模式串的j下标从next[j]开始 
    45         }
    46     }
    47     if(j >= tLength)
    48         return i - tLength;
    49     else 
    50         return 0;
    51 }
    52 
    53 int main()
    54 {
    55     getNext(s, next);
    56     printf("%d", 1 + KMP(s, t));
    57     return 0;
    58 }
  • 相关阅读:
    [Node.js]连接mongodb
    Vue中computed,methods,watch用法上的异同
    Vue中computed,methods,watch用法上的异同
    Vue method与computed的区别
    60分钟学会使用Node.js+Express+Ejs+mongoDB
    vue.js移动端app实战3:从一个购物车入门vuex
    基于Vue + Node.js + MongoDB的图片上传组件,实现图片的预览和删除
    【Java】线程的 6 种状态
    【Java】线程的创建方式
    如何愉快地通过命令安装Python库
  • 原文地址:https://www.cnblogs.com/WindSun/p/10211073.html
Copyright © 2020-2023  润新知