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

    模板题:

    给出两个字符串A和B,其中A为B的子串,求出A在B中所有出现的次数。

    首先,字符串匹配的朴素算法就是枚举B串的每一个元素,然后当 \(B[i]\)\(A[1]\) 相等时,在枚举 \(B[i+1]\)\(A[2]\)...依次类推,直到A和B的子串完全匹配是输出A所在的位置就OK啦

    But,它的时间复杂度是 \(O(nm)\)(n,m是字符串A,B的长度)

    当n,m超过一定程度是就要T了

    所以KMP出现了


    KMP算法是一种可以在线性时间内,判断字符串A是否为字符串B的子串,并求出A在B出现的位置的神奇算法(反正很高级就好了)

    不过字符串Hash也可以在 \(O(n)\) 的时间内预处理,并且支持 \(O(1)\) 查询,不过好像莫得KMP方便


    下面是KMP与暴力的区别

    \(B[i]\)\(A[j]\) 匹配失败后,i只向后移动了一个,这就浪费了很多时间,所以KMP出现了

    我们可以预处理出一个next数组,记录匹配失败后下一次的匹配的 \(i\)(如何处理等会讲),然后就可以跳过中间的无用的匹配,就可以节约很多时间

    这就好比打游戏的时候,你在Boss关输了,本来要重新开始的,但是你在中途存档了,就可以直接跳关,节约时间

    (算阶还有一个 \(f[i]\) 表示B中以 \(i\) 结尾的子串与A的前缀能够匹配的最长长度,这个锅最后补(手动尴尬))

    下面进入next数组的计算方法


    首先 \(next[i]\) 表示A中以 \(i\) 结尾的非前缀子串与A的前缀能匹配的最长长度

    很显然对吧,如果你能看懂的话,那真是太好了,因为你可以跳过下面的理解教程

    手动理解中

    红色的是以i结尾的非前缀子串,绿色的是A的前缀

    所以next数组就是红色为绿色的真子集(绿色包含红色,但绿色不等于红色)时,红色的最长长度

    (画个图就好理解多了)

    =理解完毕=

    怎么用代码实现呢。。。

    某蒟蒻lcg(举手):枚举 \(j\)\(i\) 的大小,并查找 \(A[1\sim j]\)\(A[i-j+1\sim i]\) 是否相等

    很好,某个菜鸡已经讲出了\(O(n^2)\)的预处理,我们得到了一个比暴力还慢的预处理了

    如何在更短的时间里求出next数组呢?

    我们首先要证明当 \(next[i]\) 可以为 \(j\) 时,小于 \(j\) 能选择的最大值为 \(next[j]\)

    证明:易证,证毕

    证明:(不会反证法的左转)

    假设有 $next[j_0]<j_1<j_0,使 \(next[i]\) 可以选择 \(j_1\)

    如图所示:

    明显可以看出 \(next[j_0]\) 是包含在 \(next[j_1]\) 里的

    又因为next数组表示在当前位置能匹配到的最大值,而 \(j_1\)\(next[j_0]\) 更优,这与next的定义矛盾,所以假设不正确

    因此

    \(next[i]\)可以为\(j\)时,小于\(j\)能选择的最大值为\(next[j]\)

    然后就可以进行预处理,在求\(next[i]\)时,我们只需要在\(next[i-1]+1,next[next[i-1]]+1...\)找符合要求的值就OK了

    代码实现:

    初始化\(next[1]=0\),不断尝试匹配\(j\),如果失败,使\(j=next[j]\),直到\(j==0\);如果匹配成功则\(j++\)

    next[1]=0;
    for(int i=2,j=0;i<=n;i++){
    	while(j>0&&b[i]!=b[j+1]) j=next[j];
    	if(b[i]==b[j+1]) j++;
    	next[i]=j;
    }
    

    下面是字符串的比较了(顺带说一下f数组)

    for(int i=1,j=0;i<=m;i++){
    	while(j>0&&(j==n||b[i]!=a[j+1])) j=next[j];
    	f[i]=j;//直接赋值即可
    	if(j==n) ans++;//匹配成功
    }
    

    最后输出ans即可

  • 相关阅读:
    glog Windows Visual Studio 2013 编译项目
    Git Tag管理发行版本
    Ubuntu 16.04环境中读取XBOX 360手柄信息
    GCC 中 的pie和fpie选项
    CMakeLists.txt 常用指令说用
    chrome无法访问github.com
    删除前n天的数据
    shell(9)秒转换为时分秒
    Drools规则引擎实践直白总结
    空闲时间研究一个小功能:winform桌面程序如何实现动态更换桌面图标
  • 原文地址:https://www.cnblogs.com/LCGUO/p/12296600.html
Copyright © 2011-2022 走看看