字符串匹配:从后缀自动机到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节省内存:

字符串匹配:从后缀自动机到KMP

时间: 2024-08-03 19:49:38

字符串匹配:从后缀自动机到KMP的相关文章

字符串匹配的BF算法和KMP算法学习

引言:关于字符串 字符串(string):是由0或多个字符组成的有限序列.一般写作`s = "123456..."`.s这里是主串,其中的一部分就是子串. 其实,对于字符串大小关系不如是否相同重要.包括密码验证.hash列等. 而字符串的存储结构有两种:顺序存储结构和链式存储结构.由于不同的字符是连在一起的,所以一般是开足够大的空间进行顺序存储,这样更符合字符串的意义. 一.BF算法实现 一种暴力的.朴素的模式匹配算法,是的,时间复杂度为O(M*N).而下面的KMP算法则是O(M+N)

字符串匹配(三)----后缀数组算法

一.什么是后缀数组: 字符串后缀Suffix 指的是从字符串的某个位置开始到其末尾的字符串子串.后缀数组 Suffix Array(sa) 指的是将某个字符串的所有后缀按字典序排序之后得到的数组,不过数组中不直接保存所有的后缀子串,只要记录后缀的起始下标就好了. 比如下面在下面这张图中,sa[8] = 7,表示在字典序中排第9的是起始下标为7的后缀子串,这里还有一个比较重要的数组rank,rank[i] : sa[i]在所有后缀中的排名 ,比如rk[5]=0,表示后缀下标为5的子串在后缀数组中排

【字符串匹配】AC自动机模板

AC自动机 struct ACauto { int ch[MAXN][26]; int size; int f[MAXN],last[MAXN],val[MAXN],cnt[MAXN]; void init()//初始化 { size=1; memset(ch[0],0,sizeof(ch[0])); memset(cnt,0,sizeof(cnt)); //用于统计配对数 } int idx(char c)//用于返回编号 { return c-'a'; } void insert(char

关于后缀自动机的一些理解

咕咕咕了好久的东西,以前只是粗糙的背个板子之类的,并未对parent树有什么深刻的理解 clj姐姐tql,这么神仙的东西是怎么想出来的啊 个人认为有以下几点是难以理解的: 一.right集合的定义: right集合就是指某一子串在原串当中出现的所有的位置的集合,也在某位大佬博客中叫做endpos集合: 二.right集合(endpos集合)相同的我们称为endpos等价类(重点理解) 三.对于任意两个endpos集合中,那么必然为其中一个为另一个的子串,或者两者绝无交集 四.endpos的等价类

字符串匹配:从机器到后缀自己主动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匹配. 内存使用 可能多个tran

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

字符串匹配 - KMP算法

首先大致的学习一下有限自动机字符匹配算法,然后在讨论KMP算法. 有限自动机 一个有限自动机M是一个五元组(Q,q0,A,Σ,δ),其中: Q是状态的集合, q0∈Q是初始状态, A是Q的字集,是一个接受状态集合, Σ是一个有限的输入字母表, δ是一个从Q×Σ到Q的函数,叫做转移函数. 下面定义几个相关函数: φ(w)是M在扫描字符串w后终止时的状态.函数φ有下列递归关系定义:φ(ε) = q0,φ(wa) = δ(φ(w),a), σ(x)是x的后缀中,关于P的最长前缀的长度. 字符串匹配自动

字符串匹配KMP算法的理解(详细)

1. 引言 本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP,思路混乱导致写也写得混乱.所以一直想找机会重新写下KMP,但苦于一直以来对KMP的理解始终不够,故才迟迟没有修改本文. 然近期因开了个算法班,班上专门讲解数据结构.面试.算法,才再次仔细回顾了这个KMP,在综合了一些网友的理解.以及算法班的两位讲师朋友曹博.邹博的理解之后,写了9张PPT,发在微博上.随后,一不做二不休,索性将PPT上的内容整理到了本文之中(后来文章越写越完整,所含内容早已不再是九张PPT 那样简单

字符串匹配的KMP算法

html, body { font-size: 15px; } body { font-family: Helvetica, "Hiragino Sans GB", 微软雅黑, "Microsoft YaHei UI", SimSun, SimHei, arial, sans-serif; line-height: 1.6; color: ; background-color: ; margin: 0; padding: 16px 20px; } h1, h2, h