[字符串] kmp与trie与ac自动机的快乐一家人
Posted zero_orez6
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[字符串] kmp与trie与ac自动机的快乐一家人相关的知识,希望对你有一定的参考价值。
kmp
kmp算法,主要用于进行字符串之间的模式匹配,也就是判断字符串b是否为a的子串,并支持在线性的时间内找出b在a中的出现次数。
具体实现
讲完主要功能,当然要开始讲如何去实现了。
通常的,判断一个字符串是否为另一个字符串的子串,就是逐字逐字地去判断,若有不相同的字符,就将b字符串整体向后平移一位,再次从头开始判断。
下面介绍kmp算法的主要步骤:
- 先对母串a进行处理,求出一个数组 n e x t [ n ] next[n] next[n],其中 n e x t [ i ] next[i] next[i]表示“字符串a 1~i位中后缀能够与前缀匹配的最长长度”(字符串默认从1开始)
- 然后对字符串a,b进行匹配,求出一个数组f,表示“b中以i结尾的子串”与“a的前缀”所能够匹配的最大长度。
很多同学就要问了,你这求前缀与后缀的最长匹配,不也是 O ( n 2 ) O(n^2) O(n2)的吗?博主骗人!!!
下面,我们就主要讨论如何在线性时间内求出next数组。
求出next
朴素的算法当然就是像判断子串那样,一个一个平移判断, O ( n 2 ) O(n^2) O(n2),就不多讲了。
先对母串a进行自我匹配
-
先将匹配的下标j,赋初值为0,也就是字符串前一位
-
再一边枚举a 1 _1 1数组,一边将j+1位与现在枚举的位数进行匹配,分为以下两种情况:
若两者相同,则令j++.
否则令j=next[j],再次进行判断
3.最后令next[i]=j就行了。
下面解释原理:
-
为什么令j=next[j]???
因为next数组中存储的是前缀与后缀能够匹配的最长长度,也就是说,假如 n e x t [ i ] = k next[i]=k next[i]=k,那么字符串a中 a [ 1 a[1 a[1~ k ] k] k] == a [ i − k a[i-k a[i−k~ i ] i] i],那么我们将 j j j移到 n e x t [ j ] next[j] next[j],与前next[next[j]]位进行匹配,在前缀匹配上是等价的,只有后面的字符不相同,对后面的字符再次进行判断。
-
为什么相同时令j++???
因为前缀和后缀又多匹配了一位呀,就是新枚举到的第i位。
-
为什么next[1]=0???
因为这里说的前缀和后缀的匹配是不平凡匹配,也就是不包括这个串本身,所以第一个字符只有其本身,当然是0了。
附上code
next[0]=-1;
for(int i=1;i<m;i++)
int now=next[i-1];
while((now!=-1)&&(ss[now+1]!=ss[i])) now=next[now];
next[i]=now+1;
复杂度
在每次while循环中,j的值不断减小,对于
j
=
n
e
x
t
[
j
]
j=next[j]
j=next[j]这一语句的执行次数不会超过for循环开始时的值与while结束时的值的差,在for循环中j的值至多增加1。
综上所述:j的总变化次数不超过
2
(
n
+
m
)
2(n+m)
2(n+m),忽略掉常数,求出next数组的复杂度约为
O
(
n
+
m
)
O(n+m)
O(n+m)
trie
trie,又被称为字典树,是一种支持字符串快速检索的多叉树结构。
这种树结构的每一个节点都有一个字符指针,当询问或插入一个字符串时,按这个字符串的每一个字符依次向下检索trie树。
初始化
一棵trie在开始时只包含一个根节点,字符指针指向此根节点。
插入
插入一个字符串时,依次遍历该字符串的每一个字符,若在trie树中存在该字符,则使字符指针指向该节点,并沿继续向下检索,否则新建一个节点q,使字符指针指向该节点。
检索
检索操作与插入大致相同,只是在字符指针指向空时,返回0即可。
为便于大家理解,附上草图:
如图是插入cat,car和dog的一棵trie树
附上code:
void add(char* s)//插入操作
int len=strlen(s),p=1;
for(int k=0;k<len;k++)
int ch=s[k]-'a';
if(trie[p][ch]==0) trie[p][ch]=++tot;
p=trie[p][ch];
end[p]=1;
bool ask(char* s)//检索操作
int len=strlen(s),p=1;
for(int k=0;k<len;k++)
p=trie[p][s[k]-'a'];
if(p==0) return 0;
return end[p];
值得注意的几点
- 对于插入字符串的每一个字符,他们在trie树中其实是若干条边,也就是所说的指针
- 树的节点上记录一些关于字符串的额外信息,例如上图中存储的是单词末尾的标记,我们还可以根据题目要求改变存储信息。
- 在将char类型与int 类型相互转换时注意题目要求是大写字母,小写字母或是两者都有。
ac自动机
观察题目可得,ac自动机是上面两者结合的产物
以上是关于[字符串] kmp与trie与ac自动机的快乐一家人的主要内容,如果未能解决你的问题,请参考以下文章