zoukankan      html  css  js  c++  java
  • KMP

    字符串匹配的朴素算法,我就不讲了,我们直接进入KMP算法

    KMP算法的优化在哪里

    正常的朴素算法n^2是枚举每一个字符串每一个点为起点,与模板进行比对,比对一次是O(m)的复杂度
    所以总复杂度就是O(nm),这样的复杂度显然不符合要求,但我们可以发现对于这样的例子:



    我们在发现了第四位B和D不一样了以后我们不是单纯的将起点右移一位,此时 i=4,j=3,
    将a[i]与a[j+1]比较发现不同,我们将j返回到模板中nxt[j] (nxt数组将在后文讲到),j于是变成1


    我们再次比较a[i]与a[j+1]发现相同,再i++,继续判断,以此类推

    那么nxt[j]是什么以及我们应该如何预处理出模板中nxt[j]
    1. 首先,对于一个字符串,我们要了解两个概念:”前缀”和”后缀”。

    “前缀”指除了最后一个字符以外,一个字符串的全部头部组合;”后缀”指除了第一个字符以外,一个字符串的全部尾部组合。
    对于字符串:"bread"
    前缀: b , br , bre, brea
    后缀: read, ead, ad , d

    2. 那么知道了什么是前缀后缀,我们再来求nxt数组,通俗解释是”前缀”和”后缀”的最长的共有元素的长度就是nxt中存的值
    拿"ABCDABD"举例*

    nxt[1]=0    "A"的前缀和后缀都为空集,共有元素的长度为0;
    
    nxt[2]=0    "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
    
    nxt[3]=0    "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
    
    nxt[4]=0    "ABCD"的前缀为[A, AB, ABC],
    后缀为[BCD, CD, D],共有元素的长度为0;
    
    nxt[5]=1    "ABCDA"的前缀为[A, AB, ABC, ABCD],
    后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
    
    nxt[6]=2    "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],
    后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
    
    nxt[7]=0    "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],
    后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

    3.那么我们应该如何o(m)的求出模板的nxt预处理呢
    其实采用的思路和查询操作相似
    初始状态如下


    当j==0零的时候我们要进行特判,因为nxt[0]==0,它将永远出不了循环所以当a[j+1]==a[i]或者j==0时就不继续j=nxt[j]而是跳出循环
    实现如下

    inline void getn(char a[N])
    {
        for(register int i=2,j=0;i<=m;i++)
        {
            while(j&&a[j+1]!=a[i]) j=nxt[j];
            if(a[j+1]==a[i]) j++;
                nxt[i]=j;
        }
    }

    而完成了这步操作以后搜索操作也和这个差不多

    实现如下:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <map>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <ctime>
    using namespace std;
    const int N=1000100;
    char s1[N],s2[N];
    int n,m,nxt[N],cnt=0;
    inline void getn(char a[N])
    {
        for(register int i=2,j=0;i<=m;i++)
        {
            while(j&&a[j+1]!=a[i]) j=nxt[j];
            if(a[j+1]==a[i]) j++;
            nxt[i]=j;
        }
    }
    inline void search(char a[N],char b[N])
    {
        for(register int i=1,j=0;i<=n;i++)
        {
            while(j&&b[j+1]!=a[i]) j=nxt[j];
            if(b[j+1]==a[i]) j++;
            if(j==m) printf("%d
    ",i-m+1);
        }
    }
    int main()
    {
        scanf("%s%s",s1+1,s2+1);
        n=strlen(s1+1),m=strlen(s2+1);
        getn(s2);search(s1,s2);
        for(register int i=1;i<=m;i++)
            printf("%d ",nxt[i]);
        return 0;
    }
  • 相关阅读:
    C#学习资源汇总
    ASP.NET MVC从视图传参到控制器的几种形式
    ASP.NET MVC之从控制器传递数据到视图方式
    Scrapy at a glance预览
    登录views
    django加密解密api
    django静态模版使用
    django重定向
    Django 2.1.3 文档
    秀图看图 隐私声明
  • 原文地址:https://www.cnblogs.com/cold-cold/p/10052783.html
Copyright © 2011-2022 走看看