从kmp到AC自动机

知道kmp的请跳过这一段
找到最清晰的解析

kmp

我看了约114514个解析才搞懂

如何求next

首先,next[i]本应表示0~i的字符串的最长相同前缀后缀的长度。
不过为了方便匹配,实际可以存最长相同前缀后缀时前缀最后一个的地址
听起来好绕
那这么说吧:
例如串
abaabaabaab
next[0]=-1 肯定找不到
next[1]=-1 因为第一个前缀是a,它是b
next[2]=0 因为第一个前缀是a,它是a
next[3]=0 因为第一个前缀是a,它是a?
wait!不一样的点来啦!
先偷窥一下匹配的代码:

for (int i=1;i<m;i++){int j=next[i-1];while ((s[j+1]!=s[i])&&(j>=0))j=next[j];if (s[j+1]==s[i]) next[i]=j+1;else next[i]=-1;
} 

也就是
a先问了它旁边那个,发现它匹配到了0
然后他也蠢蠢欲动
但下个是b
所以他的j被打到了next[2]=-1
然后循环没了
这时,他毫不惊奇地发现下一个是a
于是他就匹配上了(合理)
next[4]=1
因为它前面已经匹配到了,所以它可能是 前面匹配到的 前缀的地址+1的 那个字母
听起来还是好绕
说白了,它可以是
b先问了它旁边那个,发现它匹配到了0
然后他也蠢蠢欲动
发现下个是b
所以循环没了
这时,他毫不(是真的)惊奇地发现下一个是b
于是
abaab(内两b匹配上了)
理解了吧?
剩下的自己推,别问我
那么,现在烤馍片完成了,该做什么呢

next的作用

以下搬运自dalao题解,此处F[i]指next[i]+1
我们还是先给出一个例子:
A="abaabaabbabaaabaabbabaab"
B="abaabbabaab"
当然读者可以通过手动模拟得出只有一个地方匹配
abaabaabbabaaabaabbabaab
我们再用i表示当前A串要匹配的位置(即还未匹配),j表示当前B串匹配的位置(同样也是还未匹配),补充一下,若i>0则说明i-1是已经匹配的啦(j同理)。
首先我们还是从0开始匹配:

此时,我们发现,A的第5位和B的第5位不匹配(注意从0开始编号),此时i=5,j=5,那么我们看F[j-1]的值:
F[5-1]=2;
这说明我们接下来的匹配只要从B串第2位开始(也就是第3个字符)匹配,因为前两位已经是匹配的啦,具体请看图:

然后再接着匹配:

我们又发现,A串的第13位和B串的第10位不匹配,此时i=13,j=10,那么我们看F[j-1]的值:
F[10-1]=4
这说明B串的03位是与当前(i-4)(i-1)是匹配的,我们就不需要重新再匹配这部分了,把B串向后移,从B串的第4位开始匹配:

这时我们发现A串的第13位和B串的第4位依然不匹配

此时i=13,j=4,那么我们看F[j-1]的值:
F[4-1]=1
这说明B串的第0位是与当前i-1位匹配的,所以我们直接从B串的第1位继续匹配:

但此时B串的第1位与A串的第13位依然不匹配

此时,i=13,j=1,所以我们看一看F[j-1]的值:
F[1-1]=0
好吧,这说明已经没有相同的前后缀了,直接把B串向后移一位,直到发现B串的第0位与A串的第i位可以匹配(在这个例子中,i=13)

再重复上面的匹配过程,我们发现,匹配成功了!

这就是KMP算法的过程。
另外强调一点,当我们将B串向后移的过程其实就是i++,而当我们不动B,而是匹配的时候,就是i++,j++,这在后面的代码中会出现,这里先做一个说明。
最后来一个完整版的(dalao:话说做这些图做了好久啊!!!!):

kmp例题代码实现:

#include <bits/stdc++.h>
using namespace std;
string s1,s2;
int n,m,i,j,_next[1000010];
int main(){cin>>s1>>s2;n=s2.size();m=s1.size();_next[0]=-1;for (i=1;i<n;i++){j=_next[i-1];while ((s2[j+1]!=s2[i])&&(j>=0))j=_next[j];if (s2[j+1]==s2[i]) _next[i]=j+1;else _next[i]=-1;}i=0;j=0;while (i<m) {if (s1[i]==s2[j]) {i++;j++;if(j==n)cout<<i-n+1<<endl,j=_next[j-1]+1;}else{if (j==0)i++;else j=_next[j-1]+1;}}for (i=0;i<n;i++){cout<<_next[i]+1<<" ";}return 0;
}

c,怎么这么难调
知道Trie的请跳过这一段
网上的解析

Trie

大概论述一下过程:

  1. 选定要加入到Trie树中的字符串
  2. 从根节点开始依次判断当前结点的子节点中是否包含下一个字符
  3. 如果包含,则直接访问,重复第2步
  4. 否则,则建立这个结点,继续重复第2步
  5. 若进行第2步时已经到达了最后一个字符,则直接结束
    那么,我们来举个例子
    假如,我要构建成字典树的单词是her hen hers say said
    最终构建完的字典树就长这样:
   root/   \h   s/     \e      a
/ \    / \
r n    y i
|        |
s        d

然而我们并不知道这些东西分别代表那些字符串
于是,我们对每个字符串的结尾所在的那个节点加个标记。
于是乎,我们从根节点开始,一层层依次遍历,当读入到一个加了标记的结点时,一路读到的字符连成的字符串便是原来需要储存的字符串
那么,至于怎么加入一个串呀。。。
我们也来演示一下吧、。。。
比如当前这样子:

   root/   \h   s/     \e      a
/ \    / 
r n    y 
|        
s        

其他的串都已经加入,我们现在需要加入字符串said
那么,我们从根节点开始

   ro_ot(用_表示当前节点)/   \h   s/     \e      a
/ \    / 
r n    y 
|        
s        

发现根节点的子节点里面存在s这个字符结点
把一个指针移动过去,继续找接下来的字符

   root/   \h   _s/     \e      a
/ \    / 
r n    y 
|        
s        

接着,我们惊奇的发现a也存在了,于是继续遍历

   root/   \h   s/     \e      _a
/ \    / 
r n    y 
|        
s        

这个时候,却发现当前结点不存在一个i结点,那么,我们就手动的造一个i结点出来

   root/   \h   s/     \e      a
/ \    / \
r n    y i
|        
s        

同理:

   root/   \h   s/     \e      a
/ \    / \
r n    y i
|        |
s        d

OK,上代码!

#include <bits/stdc++.h>
using namespace std;
int n,end=0;
vector<int> fa(100005),son(100005),nth(100005),sa[26](100005);//窝喜欢用vector,别喷窝啊……
void build(int where,string s,int now){if(now>=s.size())return;if(son[where]&(1<<int(c[now]-'a')))build(sa[int(c[now]-'a')][where],s,now+1);else{end++;fa[end]=where;son[where]|=(1<<int(c[now]-'a'));sa[int(c[now]-'a')][where]=end;} 
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);while(cin>>n&&n){fa=nth;son=nth;fail=nth;end=0;for(int i=1;i<=n;i++){string s;cin>>s;build(0,s,0);}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/796212.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LinkedHashMap原理详解—从LRU缓存机制说起

写在前面 从一道Leetcode题目说起 首先,来看一下Leetcode里面的一道经典题目:146.LRU缓存机制,题目描述如下:请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 in…

微积分快速入门1部分:直觉

1 一分钟微积分:X射线和延时视觉 我们通常只看到图形、公式和情况的表面价值。微积分为我们提供了两种深入挖掘的超能力:X射线能看到图案中隐藏的部分。你不仅能看到树,还能知道它是由年轮组成的,在我们说话的同时,另一个年轮也在生长。延时视觉你能看到物体未来的运行轨迹…

torch.nn.Embedding的导入与导出

简介及导入转自:torch.nn.Embedding使用在RNN模型的训练过程中,需要用到词嵌入,使用torch.nn.Embedding可以快速的完成:只需要初始化torch.nn.Embedding(n,m)即可(n是单词总数,m是词向量的维度)(n是嵌入字典的大小,m是嵌入向量的维度。)。 注意: embedding开始是随机的…

第十七讲 为什么这些SQL语句逻辑相同,性能却差异巨大?

第十七讲: 为什么这些SQL语句逻辑相同,性能却差异巨大? 简概:引入: ​ 在 MySQL 中,有很多看上去逻辑相同,但性能却差异巨大的 SQL 语句。对这些语句使用不当的话,就会不经意间导致整个数据库的压力变大。我今天挑选了三个这样的案例和你分享。希望再遇到相似的问题时,…

2024-09-12 TypeError: Cannot read properties of undefined (reading 0) ==》检查未定义的对象or数组

TypeError: Cannot read properties of undefined (reading 0) ==》TypeError:无法读取undefined的属性(读取“0”) 请记住出现这种错误大多数都是因为你读取了未定义的对象或数组 排查结果:后端返回的id由原来的小写id改成了大写Id。 666,服了,哥们。

UNO 已知问题 在后台线程触发 SKXamlCanvas 的 Invalidate 且在 PaintSurface 事件抛出异常将炸掉应用

本文记录一个 UNO 已知问题,在 UNO 里面可以利用 SKXamlCanvas 对接 Skia 绘制到应用里面。如果此时在后台线程里面调用 SKXamlCanvas 的 Invalidate 触发界面的重新刷新,但在具体的执行绘制 PaintSurface 事件里面对外抛出异常,将会导致应用炸掉背景: 我准备在 UNO 里面将…

WPF 的 WriteableBitmap 在 Intel 11 代 Iris Xe Graphics 核显设备上停止渲染

在 Intel 11 代锐炬 Intel Iris Xe Graphics 核显设备上,如果此设备使用旧版本驱动,则可能导致 WPF 的 WriteableBitmap 停止渲染。此问题和 WPF 无关,此问题是 Intel 的 bug 且最新驱动版本已修复官方问题记录地址:https://www.intel.cn/content/www/cn/zh/support/articl…

WPF 的 Viewport3D 等 3D 模块在带 Intel UHD 770 设备上抛出渲染异常

在带 Intel UHD 770 的设备上,使用旧版本驱动,即小于 30.0.101.1660 版本驱动,将会导致 WPF 的 3D 模块出现渲染异常。此问题和 WPF 无关,此问题是 Intel 的 bug 且最新驱动版本已修复官方问题记录地址:https://community.intel.com/t5/Graphics/Crash-with-UHD-770-in-WP…

【Azure Service Bus】批量处理Service Bus Topic 中的死信消息(dead-lettered messages)

问题描述 在Azure的门户页面上,因为Service Bus Topic中有很多dead-lettered message,而这些消息占用了大量的存储空间,通过门户上的Service Bus Explorer每次只能消费一条消息。 虽然可以通过修改代码来指定消费私信队列中消息,但是需要修改代码,需要一些工作量。 有没有…

Transformer两大发展方向——GPT系列及BERT(一)

前面介绍了Transformer,随着其发展在NLP领域应用越来越多,在其基础上主要有两篇影响非常大的文章,一篇是GPT,另一篇是BERT。OpenAI提出的GPT采用Transformer解码器结构,一路更新迭代到了现在有了GPT-4,而Google提出的BERT采用Transformer的编码器结构。大体时间线如下图所…

RustPython简单使用

RustPython介绍 同CPython,Jpython,PyPy一样,RustPython,是使用Rust语言实现的Python解释器,支持Python3语法。 项目地址:https://github.com/RustPython/RustPython RustPython真正方便的是可以编译成Wasm文件,可以直接在浏览器中使用,示例网站:https://rustpython.g…

【解题报告】P8478 「GLR-R3」清明

我无可代替,哪怕来历已不神秘;麦克风接力,百万人就等我出席。P8478 「GLR-R3」清明 参考了出题人题解和 xcyyyyyy 大神的题解,强推前两篇。 拿到题完全没思路怎么办??? 人类智慧的巅峰,思维量的登峰造极。 换句话说就是非人题目,不过不得不说 GLR 的题是真的好,难度也…