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

    模板

    如何在目标串中找到模式串p?

    1、暴力枚举起始位置,逐位比较

    缺点:枚举太多无用位置,时间复杂度高

    Codes:

    1 int j;
    2 for(int i = 0;s[i];++ i){
    3     for(j = 0;p[j];++ j)
    4         if(s[i + j] != p[j]) 
    5             break;
    6     if(!p[j]){ //匹配成功 
    7         //Operations
    8     }
    9 }

    复杂度:O((n - m + 1) * m) (n:原串长度,m:匹配串长度)

    2、KMP

    优点:只枚举了可能有用的位置(其实就是暴力缺点opp. QwQ)

    那是如何做到只枚举有用位置的呢?

    nxt数组!

    ①nxt[i + 1] 定义为最大的 j + 1 使得 p[0~j] 是 p[0~i] 的后缀

     ②nxt[i] 定义为当 i 位置不匹配时,将跳到 nxt[i] 位置重新匹配(关于nxt数组定义众说纷纭,这里只给出我认为最易懂的定义QwQ)

    因为再不匹配时kmp算法并不是再枚举下一位,而是跳到 nxt[i] 继续匹配,所以会明显提高效率,时间复杂度降到线性(原串不回溯)

    例子1:

    P   :  0 1 2 3 4 5 6 7

            a b c a b c a d

    nxt: -1 0 0 0 1 2 3 4

    例子2:

    P   :  0 1 2 3 4 5 6 7

            a b a c b a b a

    nxt: -1 0 0 1 0 0 1 2

    例子3 :

    P   :  0 1 2 3 4 5 6 7

            a a a a a a a a

    nxt: -1 0 1 2 3 4 5 6

    清楚定义之后,计算nxt[]的思路也差不多了

    Codes:

     1 void get_nxt(char *p){
     2     int i = 0,j = -1;
     3     nxt[0] = -1; //边界 
     4     while(p[i]){
     5         if(j == -1 || p[j] == p[i]) //到达边界 或 前缀等于后缀 
     6             nxt[++ i] = ++ j; //可以跳到的位置 + 1 
     7         else j = nxt[j]; //不匹配则跳到 nxt[] 
     8     }
     9     return;
    10 }

    接下来是匹配,可以观察其与暴力的不同或相同点

    Codes:

     1 int kmp(char *p,char *s){ //传指针,可以理解为数组
     2     //s:目标串,p:模式串 
     3     int res = 0; //计数,出现次数 
     4     get_nxt(p); //处理nxt 
     5     for(int i = 0,j = 0;s[i];++ i){
     6         while(j != -1 && s[i] != p[j]) 
     7             j = nxt[j]; //若不匹配,跳到 nxt[j] 
     8         ++ j; //匹配上 
     9         if(!p[j]){ //模式串与目标串匹配完成 
    10             res ++; //计数 ++ 
    11             j = nxt[j]; //若计算重叠串则返回nxt[j] 
    12             //Operations 
    13         }
    14     } 
    15     return res;
    16 }

    模板(其实就是把上面搬过来2333)

    Codes:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<cstdio>
     5 #define N 1010
     6 
     7 using namespace std;
     8 
     9 char s[N],p[N];
    10 int nxt[N];
    11 void get_nxt(char *p){
    12     int i = 0,j = -1;
    13     nxt[0] = -1; //边界 
    14     while(p[i]){
    15         if(j == -1 || p[j] == p[i]) //到达边界 或 前缀等于后缀 
    16             nxt[++ i] = ++ j; //可以跳到的位置 + 1 
    17         else j = nxt[j]; //不匹配则跳到 nxt[] 
    18     }
    19     return;
    20 }
    21 int kmp(char *p,char *s){ //传指针,可以理解为数组
    22     //s:目标串,p:模式串 
    23     int res = 0; //计数,出现次数 
    24     get_nxt(p); //处理nxt 
    25     for(int i = 0,j = 0;s[i];++ i){
    26         while(j != -1 && s[i] != p[j]) 
    27             j = nxt[j]; //若不匹配,跳到 nxt[j] 
    28         ++ j; //匹配上 
    29         if(!p[j]){ //模式串与目标串匹配完成 
    30             res ++; //计数 ++ 
    31             j = nxt[j]; //若计算重叠串则返回nxt[j] 
    32             //Operations 
    33         }
    34     } 
    35     return res;
    36 }
    37 int main(){
    38     scanf("%s%s",s,p);
    39     cout << kmp(p,s) << '
    '; 
    40     return 0;
    41 }

     复杂度:O(n + m)(n:原串长度,m:匹配串长度)

  • 相关阅读:
    Python解决编码问题: `UnicodeEncodeError: 'gbk' codec can't encode character 'xa0' in position 10
    python中关于windows文件名非法字符的过滤处理
    IDEA 卡住半天,buid(编译)不动——解决办法(适用于maven和gradle)及定位全过程
    Linux 系统常见压缩文件(.deb、.rpm等)解压记录
    Linux Redis 安装异常处理
    从hdfs导入数据到hive表
    Python:Rocketmq消息队列使用
    Linux的nohup命令使用 —— 在服务器后台一直执行程序
    kafka:安装和命令行使用
    kafka报错:kafka.errors.NoBrokers Available,Close of session 0x100457e83740000 java.io.IOException 和 The broker is trying to join the wrong cluster
  • 原文地址:https://www.cnblogs.com/Loizbq/p/7635823.html
Copyright © 2011-2022 走看看