zoukankan      html  css  js  c++  java
  • 字符串匹配:从机器到后缀自己主动KMP

    后缀自己主动机(sam)对字符串匹配
    ====
    我们已经配置了一个相对较短的模式字符串sam。


    为P="abcabcacab", T[1..i]后缀。因此,它是sam最长前缀长度:
    T: b a b c b a b c a b c a a b c a b c a b c a c a b  c
       1 1 2 3 1 1 2 3 4 5 6 7 1 2 3 4 5 6 7 5 6 7 8 9 10 4
    假设最长前缀长度是|P|,则表示T[1..i]的后缀和P匹配。




    内存使用
    可能多个trans指针同一个节点。因此像删除树那样会引起double-free:
    为此我们临时採用内存池的做法。
    假设扩展到包含数字和空格,则须要表示37个转移指针。




    KMP算法
    ====
    给定模式串P,文本串T,
    如果在s位置已匹配了q个字符, 即P[1,..,q]=T[s+1,..,s+q], 而在P[q+1]不匹配。


    strstr()这时会把指针指向s+2,从P[1]又一次開始匹配。


    当时Knuth,Morris,Pratt就想可不能够把指针再移远一点。


    如果有P[1,..,k]=T[s+q+1-k,..,s+q],这时从P[k+1]開始比即可了,显然我们希望k越大越好,相应地指针移动增量=q-k越小,因此应该不会错过某些全然匹配的位置。
    我们把上面两个等式合并,得到P[1,..,k]是P[1,..,q]的后缀。

    问题变成:
    对于每一个q, 求P[1,..,q]的最长的真前缀(长度记为k),同一时候它也是P[1,..,q]的后缀。
    我们定义前缀函数pi(q):=k.


    怎样计算pi(q) ?
    ====
    使用递推的想法,如果我们已经计算好了pi(q)=k。
    假设P[k+1] = P[q+1], 则显然有pi(q+1) = k+1;
    否则,看作是一个匹配问题, 我们来看pi(q)的含义是与P[1,..,q]末尾匹配的最长前缀长度k,我们就拿这个前缀来匹配,并期望P[k+1]和P[q+1]一样,否则k=pi(k)循环下去。


    初始条件:pi(1) = 0, 由于最长真前缀是空串。


    当前P[1,..,k]匹配T[q-k+1,q],而在T[q+1]不匹配。
    应用前缀函数的定义。应该从位置s+1-k + q-k = 
    一个字符串P[1,..,j]去匹配P[q+1-j,..q+1]的过程。


    k=pi(k), 直到P[k] = P[q+1]。


    怎样做线性的字符串匹配?
    ====
    參照后缀自己主动机的做法, 我们把pi和P组成一个自己主动机,T在这个自己主动机上
    走一遍。




    前缀函数练习题目:
    1. P 在T中的出现次数? 提示:检查pi(PT)
    2. (ab)^3 = ababab, 怎样求最大的反复因子r=3?

    3. 怎样在线性时间内推断是否为循环移位,比方arc和car。(这个我还不知道怎么做)



    KMP比SAM节省内存:





    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    如何快速修改替换对象中的某个属性?
    element组件 MessageBox不能显示确认和取消按钮,记录正确使用方法!
    记录一下vue transition 过渡各状态()
    记录一下vue slot
    vue路由传参query和params的区别(详解!)
    一段话让你理解vuex的工作模式!
    vue+axios访问本地json数据踩坑点
    怎么构建vue-cli项目
    IO模型
    epoll真正实现高并发服务器
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4717586.html
Copyright © 2011-2022 走看看