热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:AC自动机解决字符集很大的情况(可持久化数组优化getfail的过程)

篇首语:本文由编程笔记#小编为大家整理,主要介绍了AC自动机解决字符集很大的情况(可持久化数组优化getfail的过程)相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了AC自动机解决字符集很大的情况(可持久化数组优化getfail的过程)相关的知识,希望对你有一定的参考价值。






今天遇到了一个问题,那就是如果




A


C



AC


AC
自动机的字符集很大该怎么办?比如改成




1


e


5



1e5


1e5
该怎么办呢?

例如下题:
在这里插入图片描述
题目来源转自(侵权删):点击查看

先不考虑解法,肯定是需要用




A


C



AC


AC
自动机去解决的,但是问题是,本题中字符串的总长度可能达到




O


(



n


2



)



O(n^2)


O(n2)
级别,加上字符集特别大,所以遇到了很多问题

不过不难看出题目中给出的实际上就是一棵




t


r


i


e



trie


trie
树,我们可以直接在




t


r


i


e



trie


trie
树上




g


e


t


f


a


i


l



getfail


getfail
,就可以构建出




A


C



AC


AC
自动机了,现在的问题转换为,如何处理字符集很大的情况

先看普通的




g


e


t


f


a


i


l



getfail


getfail
的模板:

void getfail()
{
queue<int>q;
for(int i&#61;0;i<26;i&#43;&#43;)
{
if(trie[0][i])
{
fail[trie[0][i]]&#61;0;
q.push(trie[0][i]);
}
}
while(!q.empty())
{
int cur&#61;q.front();
q.pop();
for(int i&#61;0;i<26;i&#43;&#43;)
{
if(trie[cur][i])
{
fail[trie[cur][i]]&#61;trie[fail[cur]][i];
q.push(trie[cur][i]);
}
else
trie[cur][i]&#61;trie[fail[cur]][i];
}
}
}

简单的思路肯定是直接把




26



26


26
改成




1


e


5



1e5


1e5
&#xff0c;很可惜这样显然是会




T


L


E



TLE


TLE

考虑




b


f


s



bfs


bfs
转移的瓶颈出现在哪里呢&#xff1f;通过分析后不难看出实际上就是上面代码第




24



24


24
行的

trie[cur][i]&#61;trie[fail[cur]][i];

的这句代码&#xff0c;转移了太多无用的状态。对于每一层的转移&#xff0c;实际上只有很少的情况进入到了




i


f



if


if
语句里

所以假设&#xff0c;我们开一个




u


n


o


r


d


e


r


e


d


_


m


a


p


<


i


n


t


,


i


n


t


>


t


r


a


n


s


[


N


]



unordered\\_maptrans[N]


unordered_map<int,int>trans[N]
用于储存题目中给出的字典树&#xff0c;其中




t


r


a


n


s


[


u


]


[


t


o


]


&#61;


v



trans[u][to]&#61;v


trans[u][to]&#61;v
表示在字典树中从点




u



u


u
经过一条权值为




t


o



to


to
的边可以到达点




v



v


v
。再开一个




u


n


o


r


d


e


r


e


d


_


m


a


p


<


i


n


t


,


i


n


t


>


t


r


i


e


[


N


]



unordered\\_maptrie[N]


unordered_map<int,int>trie[N]
用于储存




g


e


t


f


a


i


l



getfail


getfail
的转移过程&#xff0c;则将上述代码改进一下&#xff1a;

void getfail()
{
queue<int>q;
for(auto it:trans[0])
{
int u&#61;0,v&#61;it.second,to&#61;it.first;
trie[u][to]&#61;v;
fail[v]&#61;u;
q.push(v);
}
while(!q.empty())
{
int u&#61;q.front();
q.pop();
trie[u]&#61;trie[fail[u]];
for(auto it:trans[u])
{
int v&#61;it.second,to&#61;it.first;
trie[u][to]&#61;v;
fail[trie[u][to]]&#61;trie[fail[u]][to];
q.push(trie[cur][to]);
}
}
}

如此一来&#xff0c;干脆把




i


f





e


l


s


e



if-else


ifelse
分支直接删除掉了。不过又出现了新的问题&#xff0c;如何快速执行第




15



15


15
行的赋值操作呢&#xff1f;

这里就要引入可持久化数组了&#xff0c;所谓可持久化数组&#xff0c;就是可以访问历史版本的&#xff0c;支持单点更新和单点查询的主席树

如此一来就完美解决了这个模型

代码&#xff1a;

const int N&#61;1e6&#43;100;
const int M&#61;1e5;
unordered_map<int,int>trans[N];
struct Node {
int l,r,val;
}tree[N];
int trie[N],fail[N],tot;
int newnode() {
tot&#43;&#43;;
tree[tot].l&#61;tree[tot].r&#61;tree[tot].val&#61;0;
return tot;
}
void add(int &k,int pos,int val,int l,int r) {
int nk&#61;newnode();
tree[nk]&#61;tree[k];
k&#61;nk;
if(l&#61;&#61;r) {
tree[k].val&#43;&#61;val;
return;
}
int mid&#61;(l&#43;r)>>1;
if(pos<&#61;mid) {
add(tree[k].l,pos,val,l,mid);
} else {
add(tree[k].r,pos,val,mid&#43;1,r);
}
}
int ask(int k,int pos,int l,int r) {
if(l&#61;&#61;r) {
return tree[k].val;
}
int mid&#61;(l&#43;r)>>1;
if(pos<&#61;mid) {
return ask(tree[k].l,pos,l,mid);
} else {
return ask(tree[k].r,pos,mid&#43;1,r);
}
}
void getfail() {
queue<int>q;
for(auto it:trans[0]) {
int u&#61;0,v&#61;it.second,to&#61;it.first;
add(trie[u],to,v,1,M);
fail[v]&#61;u;
q.push(v);
}
while(q.size()) {
int u&#61;q.front();
q.pop();
trie[u]&#61;trie[fail[u]];
for(auto it:trans[u]) {
int v&#61;it.second,to&#61;it.first;
int delta&#61;v-ask(trie[u],to,1,M);
add(trie[u],to,delta,1,M);
fail[v]&#61;ask(trie[fail[u]],to,1,M);
q.push(v);
}
}
}
void init() {
tot&#61;-1;
newnode();
}





推荐阅读
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • Java编程实现邻接矩阵表示稠密图的方法及实现类介绍
    本文介绍了Java编程如何实现邻接矩阵表示稠密图的方法,通过一个名为AMWGraph.java的类来构造邻接矩阵表示的图,并提供了插入结点、插入边、获取邻接结点等功能。通过使用二维数组来表示结点之间的关系,并通过元素的值来表示权值的大小,实现了稠密图的表示和操作。对于对稠密图的表示和操作感兴趣的读者可以参考本文。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了一种图的存储和遍历方法——链式前向星法,该方法在存储带边权的图时时间效率比vector略高且节省空间。然而,链式前向星法存图的最大问题是对一个点的出边进行排序去重不容易,但在平行边无所谓的情况下选择这个方法是非常明智的。文章还提及了图中搜索树的父子关系一般不是很重要,同时给出了相应的代码示例。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
author-avatar
白小貓貓兒是青咔咔-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有