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

    字符串匹配中经常会用到KMP算法。它求解的问题类型是:字符串匹配。给你两个字符串,寻找其中一个字符串是否包含另一个字符串,如果包含,返回包含的起始位置。 

    我们一般的做法是:将一个字符串(长度为n,模式串)放在另一个字符串(长度为m,主串)开始的位置,然后依次比较,如果有不匹配的字符,就将字符串往后移一位,然后依旧从头开始比较。直到找到完全匹配的位置,返回。遍历顺序从0到m-n,每次遍历,都要比较n(最多)次,所以复杂度近似为O(m*n);

    而我们今天说的这个KMP算法,可以达到O(m+n)的复杂度。

    算法基本思想:每次当匹配失败时,没有必要就移动一位,因为前面可能有已经匹配的字符了。如果模式串当前的字符存在公共前后缀,那么我可以对于每一个模式串事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

    注意最长前缀:是说以第一个字符开始,但是不包含最后一个字符

    KMP有个next数组,用来存放模式串中内部的匹配信息。举个例子,比如模式串为ababaca,长度是7,所以next[0],next[1],next[2],next[3],next[4],next[5],next[6]分别计算的是 aababaababababaababacababaca的相同的最长前缀和最长后缀的长度。由于aababaababababaababacababaca的相同的最长前缀和最长后缀是“”,“”,“a”,“ab”,“aba”,“”,“a”,所以next数组的值是[-1,-1,0,1,2,-1,0],这里-1表示不存在,0表示存在长度为1,2表示存在长度为3。这是为了和代码相对应。

    当求出模式串的next数组之后,我们就要进行KMP算法的匹配了。

    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    void cal_next(string str,vector<int>&next)
    {
        int len=str.size();
        next[0]=-1;
        int k=-1;
        for(int i=1;i<len;i++)
        {
            while(k>-1 && str[k+1]!=str[i])
            {
                k=next[k];
            }
            if(str[k+1]==str[i])
            {
                k++;
            }
            next[i]=k;
        }
    }
    int KMP(string str,string ptr,vector<int>&next)
    {
        int s=0,p=0;
        while(s<str.size() && p<ptr.size())
        {
            if(str[s]==ptr[p])
            {
                s++;
                p++;
            }
            else
            {
                if(p==0)
                    s++;
                else
                    p=next[p-1]+1;
            }
        }
        return (p==ptr.size())?(s-ptr.size()):-1;
    }
    int main()
    {
        string str,ptr;
        cin>>str>>ptr;
        vector<int> next;
        next.resize(ptr.size(),0);
        cal_next(ptr,next);
        cout<<KMP(str,ptr,next)<<endl;
        return 0;
    }

    程序的关键在于如何理解next的作用,以及如何维护它。

  • 相关阅读:
    [VB]用API打开浏览文件夹对话框,选择文件夹
    C# 16进制与字符串、字节数组之间的转换
    DIV未知高度的垂直居中代码
    Webbrowser控件判断网页加载完毕的简单方法
    一些控制鼠标的例子!
    Kernel device tree: simplebus
    Display Serial Interface (From WIKI)
    消费提示:常见 处理器/显卡 性能排名网站 汇总
    HDMI notes From HDMI wiki
    Linux graphics stack 理解
  • 原文地址:https://www.cnblogs.com/mini-coconut/p/9534822.html
Copyright © 2011-2022 走看看