• 终结字符串匹配----------KMP算法


    对于KMP很早之前就学过,但是一直没写博客,当再次用到时,发现忘的差不多了,所以题主花了两个小时重新拾取了一下,现在打算留下笔记,写下自己对KMP算法的理解。(在看本文章之前需要对KMP大致的理解,本文章只针对Next数组的求法+代码解析)

    第一个问题:

      字符串1:  abacabababacababc

      字符串2:  abacababc

        

        在字符串1中查找字符串2出现的次数,常规思路,暴力,时间复杂度:O(n2),只能解决1000长度以内的字符串。

    暴力算法因为存在指针回溯,导致复杂度偏高。暴力基础上解决指针回溯就是KMP算法,时间复杂度:O(n)

    学习KMP算法前,需要先学习下什么是最长公共前后缀。

    假设字符串长度为len,    即str【0】 ~  str【len-1】  , 设定 K在区间 【0,len-1】内的任意整数

    字符串前缀:包含首字符在内的子串。         即  子串:  str【0】 ~ str【k】

    字符串后缀:包含尾字符在内的子串。    即  子串:  str【k】 ~ str【len-1】

    公共前后缀:即字符串前缀完全等于字符串后缀。

    最长公共前后缀:   公共前后缀加个最长条件即可。

    那么来求字符串abacababc 的next。

    第一步分离出全部的字符串前缀。

    第二步:将得到最长公共前后缀长度表向下移一位,末尾舍去,首位填-1,得到的数组就是next数组。(如下图)

    那么得到了next的数组有什么用呢?我们模拟一遍匹配就好了

    还是上面的例子:

      字符串1:  abacabababacababc

      字符串2:  abacababc

    我们得到的字符串2的next数组为 {-1,0,0,1,0,1,2,3,2};

    首先暴力匹配, 设定变量i是指向字符串1待匹配的元素,  变量k指向字符串2待匹配元素

    回退如下

    不难看出:

        当不成功匹配的时,则k回退,k值等于对应的next的值。

      即:next数组的作用:  当发现不匹配时,k回退到对应的next值,即next值之前的元素都已经记忆匹配,如上图绿色下划线部分。

    代码解析

     next:

     1 void GetNext(){   //next数组求法 
     2     int i=0,k=-1;
     3     Next[0]=-1;
     4     while (i<m){
     5         if (k==-1||st[i]==st[k]){
     6             i++;
     7             k++;
     8             Next[i]=k;
     9         }
    10         else
    11             k=Next[k];
    12     }
    13 }

    唯一不好理解的应该是 第11行的 k=Next【k】,为什么要这样回退?

    如下例子。

    KMP:

     1 int KMP(char str[],char st[]){ //不存在输出-1,存在则输出首地址 
     2     GetNext();
     3     int i=0,j=0,n=strlen(str),m=strlen(st);
     4     while (i<n&&j<m){
     5         if (j==-1||str[i]==st[j]){
     6             i++;
     7             j++;
     8             
     9         }
    10         else
    11             j=Next[j];
    12     }
    13     if (j>=m)    return i-j+1;
    14     return -1;
    15 }

     附上签到题:

    hdu1711

     1 #include<iostream>
     2 using namespace std;
     3 
     4 
     5 int n,m,str[1000010],st[1010],Next[10010];
     6 
     7 void GetNext(){
     8     int i=0,k=-1;
     9     Next[0]=-1;
    10     while (i<m){
    11         if (k==-1||st[i]==st[k]){
    12             i++;
    13             k++;
    14             Next[i]=k;
    15         }
    16         else
    17             k=Next[k];
    18     }
    19 }
    20 
    21 int KMP(){  
    22     GetNext();
    23     int i=0,j=0;
    24     while (i<n&&j<m){
    25         if (j==-1||str[i]==st[j]){
    26             i++;
    27             j++;
    28             
    29         }
    30         else
    31             j=Next[j];
    32     }
    33     if (j>=m)    return i-j+1;
    34     return -1;
    35 }
    36 int main(){
    37     int T;
    38     scanf("%d",&T);
    39     while (T--){
    40         scanf("%d%d",&n,&m);
    41         for (int i=0;i<n;i++)
    42             scanf("%d",&str[i]);
    43         for (int i=0;i<m;i++)
    44             scanf("%d",&st[i]);
    45         cout<<KMP()<<endl;
    46     }
    47     return 0;
    48 }

    hdu2087

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 
     5 char str[1010],st[1010];
     6 int n,m,ans,Next[1010];
     7 void GetNext(){
     8     int i=0,k=-1;
     9     Next[0]=-1;
    10     while (i<m){
    11         if (k==-1||st[i]==st[k]){
    12             i++;
    13             k++;
    14             Next[i]=k;
    15         }
    16         else k=Next[k];
    17     }
    18 }
    19 
    20 void KMP(){
    21     GetNext();
    22     ans=0;
    23     int i=0,j=0;
    24     while (i<n){
    25         if (j==-1||str[i]==st[j]){
    26             i++;
    27             j++;
    28         }
    29         else
    30             j=Next[j];
    31         if (j==m){
    32             ans++;
    33             j=0;
    34         }
    35     }
    36     return ;
    37 }
    38 int main(){
    39     while (true){
    40         scanf("%s",str);
    41         if (str[0]=='#')    break;
    42         scanf("%s",st);
    43         n=strlen(str);
    44         m=strlen(st);
    45         KMP();
    46         printf("%d
    ",ans);
    47     }
    48 
    49     return 0;
    50 }
  • 相关阅读:
    validate BST
    LC282. Expression Add Operators
    nginx统计日志命令
    iptables和firewalld命令
    nginx安装
    测试服务器IO
    规范主机名和设置最大文件进程数
    Docker安装
    MySQL/MariaDB二进制安装
    Docker原理
  • 原文地址:https://www.cnblogs.com/q1204675546/p/14019024.html
Copyright © 2020-2023  润新知