zoukankan      html  css  js  c++  java
  • 【算法•日更•第三十一期】KMP算法

    ▎前言

      这次要讲的HMP算法KMP算法很简单,是用于处理字符串的,之前一直以为很难,其实也不过如此(说白了就是优化一下暴力)。

    ▎处理的问题

      通常处理的问题是这样的:给定两个字符串s1和s2,其中s1是文本串,s2是匹配串,求s2在s1中出现的位置。

      举个例子:(方便起见,下标从1开始)比如说s1是AABAAC,s2是BA,那么s2在s1的第3个位置处出现。

    ▎暴力求解

      如何使用暴力求解这道题呢?我们只要分别暴力遍历两个字符串,然后分别比对当前字符,如果相等就继续比下去,如果不相等,那么s1回溯回没有执行这一次比对的位置,s2遍历的位置归零。

      代码如下,详见注释:

     1 #include<iostream>
     2 using namespace std;
     3 string s1,s2;int i=0,j=0;
     4 int main()
     5 {
     6         cin>>s1;cin>>s2;
     7         int len1=s1.length();int len2=s2.length();
     8         while(i<len1&&j<len2)
     9         {
    10                 if(s1[i]==s2[j])
    11                 {
    12                         i++;
    13                         j++;
    14                 }
    15                 else
    16                 {
    17                         i=i-j+1;
    18                         j=0;
    19                 }
    20         }
    21         if(j==len2) cout<<i-j;
    22         else cout<<"No answer";
    23         return 0;
    24 }

    ▎KMP算法

    『定义』

      KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特莫里斯普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。(copy自百度)

      说白了就是把暴力求解的过程优化一下。

    『算法核心』

      仔细思考一下,为什么暴力求解效率不高呢?

      这个问题很好回答,因为我们直接比对失败后就抛弃了,因此没有利用到这些匹配失败的东西,如果我们能够合理的利用,在失败的基础上继续比对,那么就可以优化了。

      KMP算法不同于暴力的地方就在于把比对失败的当前遍历的字符串准确找到一个之前信息一样的字符串,继续比对。  

      比如说s1是AABAAC,s2是AB,在遍历到AA时发现不同,那么之前相同的是A,那么跳转到下一个A所在的位置继续比对,也就是跳到了第二个A的位置,发现相同,那么就找到了。

      其余的与暴力相同,只不过跳转就成为了一大难点。

      那么我们现在来考虑如何跳转,我们可以使用一个数组(next)来存储当前信息相同的位置,那么我们可以定义两个指针(不是语法中的指针,此指针非彼指针),初始化一个在0,一个在-1。

      如果一个在-1(还没有开始比对,或者跳转到了这里)或者当前字符相同,那么就继续比对。否则就将其中一个跳转。

      总而言之看代码吧。

    『代码如下』

     1 #include<iostream>
     2 #include<cstring>
     3 using namespace std;
     4 char s1[1000],s2[1000];int i=-1,j=0,next[10000];
     5 int main()
     6 {
     7         cin>>s1;cin>>s2;
     8         int len1=strlen(s1);int len2=strlen(s2);//输入字符串 
     9         next[0]=-1;//初始化 
    10         while(j<len2-1)
    11         {
    12                 if(i==-1||s2[i]==s2[j])//-1可能是初始状态的,也可能是跳过来的 
    13                 {
    14                         i++;
    15                         j++; 
    16                         next[j]=i;//顺便记录下来 
    17                 }
    18                 else i=next[i];//跳到另一个信息一样的地方 
    19         }
    20         i=0;j=0;
    21         while(i<len1&&j<len2)//常规操作 
    22         {
    23                 if(j==-1||s1[i]==s2[j])
    24                 {
    25                         i++;
    26                         j++;
    27                 }
    28                 else j=next[j];//跳转 
    29         }
    30         if(j==len2) cout<<i-j;
    31         else cout<<"No answer";
    32         return 0;
    33 }
  • 相关阅读:
    使用DataList进行ItemDataBound时出来重复绑定
    往一个数据添加另外一个数据表里面的数据
    在ServU配置ODBC过程记录(一)
    在RowDeleting中GridViewDeleteEventArgs e的RowIndex获得值错误的问题
    在Master母版页中通过FindControl()方法获取服务器控件
    经典的间隔时间滚动新闻(图片),可控制滚动
    为枚举类型添加描述信息 this 扩展 泛型约束 位运算[转]
    SQL Server 获取插入记录后的ID(自动编号)
    Shapes
    CSSFriendlyAdapter 的一个Bug
  • 原文地址:https://www.cnblogs.com/TFLS-gzr/p/11293964.html
Copyright © 2011-2022 走看看