• KMP


    简介:

    KMP算法,适用于模式匹配,即查找模式串P在字符串S内的出现位置,其时间复杂度为O(M+N)。

    (题外话:模式匹配问题是算法竞赛的常客,但理解KMP算法具有一定难度,建议先理解BF算法后再理解KMP算法。当然,网上关于KMP算法的讲解很多,这里仅仅只是给出模板。)

    模板:

    下面给出2种求next数组的方法,其中优化next数组求法虽然使得kmp算法更快,但针对需要输出模式串p的next数组的算法题,往往多数都是要原方法求出next数组。两种方法求出的next数组有什么区别可以通过网上关于KMP算法的讲解了解到。

     1 //求出模式串p的next数组 
     2 void Next(char* p,int *next)
     3 {    
     4     int pLen = strlen(p);
     5     next[0] = -1;
     6     int k = -1;
     7     int j = 0;
     8     
     9     while (j < pLen){
    10         //p[k]表示前缀,p[j]表示后缀
    11         if (k == -1 || p[k] == p[j]) {
    12             ++k;
    13             ++j;
    14             next[j] = k;
    15         }else{
    16             k = next[k];
    17         }
    18     }
    19 }
     1 //优化next数组求法
     2 void Next(char* p, int *next)
     3 {
     4     int pLen = strlen(p);
     5     next[0] = -1;
     6     int k = -1;
     7     int j = 0;
     8     
     9     while (j < pLen)
    10     {
    11         if (k == -1 || p[k] == p[j]){
    12             ++j;
    13             ++k;
    14             //改动在下面4行
    15             if (p[j] != p[k])
    16                 //之前只有这一行
    17                 next[j] = k;   
    18             else
    19                 //为了避免出现p[j] = p[ next[j] ],需要再次递归
    20                 next[j] = next[k];
    21         }else{
    22             k = next[k];
    23         }
    24     }
    25 }

    下面给出不同版本的kmp算法代码,核心算法不变,但根据目的不同进行了相应的修改。

     1 //返回模式串p在字符串s中首次出现的位置 
     2 int KMP(char* s, char* p)
     3 {
     4     int i = 0;
     5     int j = 0;
     6     int sLen = strlen(s);
     7     int pLen = strlen(p);
     8 
     9     while (i < sLen && j < pLen){
    10         //如果j = -1,或当前字符匹配成功(即S[i] == P[j]),都令i++,j++    
    11         if (j == -1 || s[i] == p[j]){
    12             i++;
    13             j++;
    14         }
    15         //如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]     
    16         else{ 
    17             j = next[j];
    18         }
    19     }
    20 
    21     if (j == pLen)
    22         //返回匹配到的位置 
    23         return i - j+1;
    24     else
    25         //匹配失败,返回0 
    26         return 0;
    27 }
     1 //输出模式串p在字符串s中出现的所有位置 
     2 void kmp(char *s,char *p)
     3 {
     4     int pLen=strlen(p);
     5     int sLen=strlen(s);
     6     int i=0;
     7     int j=0;
     8     
     9     while(i<sLen && j<pLen)
    10     {
    11         if(j==-1 || s[i]==p[j])
    12         {
    13             i++;
    14             j++;
    15         }else{
    16             j=next[j];
    17         }
    18         
    19         if(j==pLen)
    20         {
    21             //输出匹配到的位置 
    22             cout<<i-j+1<<endl;
    23             //j进行递归,i不需要改变 
    24             j=next[j];
    25         }
    26     }
    27 }

    例题:洛谷P3375

    (一道模板题)

     1 const int Size = 1000000+7;
     2 
     3 char s[Size],p[Size];
     4 int  next[Size];
     5 
     6 void Next(char *p,int *next)
     7 {
     8     int pLen=strlen(p);
     9     int k=-1;
    10     int j=0;
    11     next[0]=-1;
    12     
    13     while(j<pLen)
    14     {
    15         if(k==-1 || p[j]==p[k])
    16         {
    17             j++;
    18             k++;
    19             next[j]=k;
    20         }else{
    21             k=next[k];
    22         }    
    23     }    
    24 } 
    25 
    26 void kmp(char *s,char *p)
    27 {
    28     int pLen=strlen(p);
    29     int sLen=strlen(s);
    30     int i=0;
    31     int j=0;
    32     
    33     while(i<sLen && j<pLen)
    34     {
    35         if(j==-1 || s[i]==p[j])
    36         {
    37             i++;
    38             j++;
    39         }else{
    40             j=next[j];
    41         }
    42         
    43         if(j==pLen)
    44         {
    45             cout<<i-j+1<<endl;
    46             j=next[j];
    47         }
    48     }
    49 }
    50 
    51 int main()
    52 {
    53     scanf("%s",s);
    54     scanf("%s",p);
    55     
    56     Next(p,next);
    57     kmp(s,p);
    58     
    59     //注:遍历从下标1开始
    60     int pLen=strlen(p);
    61     for(int i=1;i<=pLen;i++)
    62     {
    63         printf("%d ",next[i]);
    64     }
    65 }
  • 相关阅读:
    398. Random Pick Index
    739. Daily Temperatures
    779. K-th Symbol in Grammar
    698. Partition to K Equal Sum Subsets
    783. Minimum Distance Between BST Nodes
    asp.netcore 深入了解配置文件加载过程
    啥叫K8s?啥是k8s?
    Asp.NetCore轻松学-实现一个轻量级高可复用的RabbitMQ客户端
    Asp.Net Core 轻松学-一行代码搞定文件上传
    目录---Asp.NETCore轻松学系列【目录】
  • 原文地址:https://www.cnblogs.com/antarctic/p/11941689.html
Copyright © 2020-2023  润新知