zoukankan      html  css  js  c++  java
  • AC自动机

    AC自动机

    define

    tire[i].son[j] 表示第 i 个节点的字母 j 儿子 。

    tire[i].End 表示这个节点是几个字符串的最后一个字母。

    tire[i].next 失配指针。

    insert()

    void insert()
    {
    	int now=0;//当前节点
    	for(int i=1;i<=Len;i++){
    		int ch=str[i]-'a';
    		if(!tire[now].son[ch])tire[now].son[ch]=++num;//如果没出现过就新建一个
    		now=tire[now].son[ch];
    	}
    	tire[now].End++;//节点now作为了一个字符串的终止节点
    }
    

    插入很简单。

    处理失配指针

    next 是找到一个最长的后缀是自己的前缀,这个后缀不能是自己本身。

    考虑 kmp 是怎么求 next 的。

    while(j&&B[i]!=B[j+1])j=nxt[j];
    

    这个 (i)(j) ((j)(i) 短) 同时指向 2 个前缀,并且 (j)(i-1)的前缀。

    如果 (j+1) 不等于 (i) 那么令 (j=nxt[j]) ,就是说如何 (j+1) 接不上 (i) 那么考虑 (j) 的前缀 (j’) ((j’)(j) 的后缀同时也是 (i-1) 的后缀)。

    那么我们也可以在 tire 树上这么求。

    首先如何保证 j 比 i 短呢?

    可以在 tire 树上面 bfs 。

    然后枚举每个点 x 的 son[i]。

    • 如果它有 son[i] ,那么让你儿子 (i) 的失配指针指向你的失配指针的儿子 (i') ,因为你的前缀加一个字母 i 肯定是你加一个字母 i 形成的字符串的前缀。
    • 否则直接让 son[i] 指向 tire[nxt].son[i] ,和 kmp 那个同理就是说如果 (j+1) 接不上 (i) 那么考虑 (j) 的前缀 (j')

    这里有一个疑问,好像缺了个 while ,如果 (j’) 也接不上呢?

    这是一个优化,这里当然可以写一个 while 循环一步一步向上跳,但是常数有点大。

    假设写 while 时会沿着 A->B->C->D 跳。

    先假设之前的 next 和 son[i] 都是对的。

    • 再假设这一条路都接不上字母 i,那么 B,C,D 都会指向 0,然后令 (A.son[i]=B.son[i]) 这肯定也是 0,是对的。
    • 再假设 C,D 能接上,那么 B 自然指向 C ,然后令 (A.son[i]=B.son[i]) 这肯定也是 C,是对的。(因为C有儿子 i,那么它指向自己的儿子)

    这样就能一步到位。

    void prepare()
    {
    	tou=1;wei=0;
    	for(int i=0;i<26;i++)
    	if(tire[0].son[i])
    		que[++wei]=tire[0].son[i];//先把长度为1的入队。
    	while(tou<=wei){
    		int x=que[tou++];
    		for(int i=0;i<26;i++)
    		if(tire[x].son[i]){
    			tire[tire[x].son[i]].next=tire[tire[x].next].son[i];
    			que[++wei]=tire[x].son[i];
    		}else tire[x].son[i]=tire[tire[x].next].son[i];
    	}
    }
    

    计算有多少个T出现在S中

    先把所有的 T 插入 tire树 中,模仿插入的方法,拿 S 在 tire树 上跑一圈(这一圈肯定不会加入新节点,因为对于每个节点,就算后面接不上字母i,那么它的儿子都指向了他的失配节点的儿子)。

    如果跑到一个地方,发现 tire[x].End 不为-1,那么就加上他的贡献,把 tire[x].End 改成 -1,紧接着沿着它的失配指针查跑到节点 0,同时做同样的操作。(如果向上跳的过程中发现 tire[y].End 是 -1,那么就break,因为沿着它的失配指针到达的节点 (z) 都被经历过了)。

    void calc()
    {
    	int now=0;
    	for(int i=1;i<=Len;i++){
    		int ch=str[i]-'a';
    		now=tire[now].son[ch];
    		for(int t=now;t&&tire[t].End!=-1;t=tire[t].next){
    			ans+=tire[t].End;
    			tire[t].End=-1;
    		}
    	}
    }
    
  • 相关阅读:
    QT编译时 cc1plus进程占用大量内存卡死问题解决
    python import cv2 出错:cv2.x86_64-linux-gnu.so: undefined symbol
    python ImportError: No module named builtins
    OSError: libcudart.so.7.5: cannot open shared object file: No such file or directory
    二维数组和二级指针(转)
    C/C++中无条件花括号的妙用
    C语言中do...while(0)的妙用(转载)
    卸载 ibus 使Ubuntu16.04任务栏与启动器消失 问题解决
    关于Qt creator 无法使用fcitx输入中文的问题折腾
    QT error: cannot find -lGL
  • 原文地址:https://www.cnblogs.com/zYzYzYzYz/p/14448348.html
Copyright © 2011-2022 走看看