后缀自动机学习小记
来源:互联网 发布:美微网络电视柠檬tv 编辑:程序博客网 时间:2024/06/06 01:38
前言:
我学习这个东西吧是很懵的,结合了多篇论文和博客才搞懂最基础的构建,需要细细琢磨。
推荐论文:
陈立杰冬令营上的论文,有些小错误,而且我讨厌指针。
张天扬集训队的论文,里面讲了许多应用。
推荐博客:
后缀自动机学习总结——functioner.
后缀自动机的定义:
我也没搞清楚。
有限状态自动机的就是能识别字符串。
而后缀自动机就能识别一个字符串的所有后缀,当然同时能识别子串。
后缀自动机的构建:
一个显然的想法是把所有后缀扔到一个叫AC自动机的东西里去。
时间复杂度:…
空间复杂度:…
你懂的~~
我们要构建的自动机自然不是上面这种劣质产品,而是最简状态的后缀自动机。
有多简呢?时空复杂度:O(n)
*接下来的构造讲的非常简略,可以观赏。
son[N][26]表示子节点。
pre[N]表示上一个能接受后缀的点。
step[N]表示从root到这个点的最长距离。
last表示最后一个能接受后缀的点。
假设现在的字符串是T,要在T的后面加一个x.
可以新建一个np代表x。
last沿着pre链条,如果son里没有x所代表的字符,就指向np。
直到点p,p的x所代表字符的子节点已经有了,设这个点为q,此时不能直接指过去,因为会覆盖掉q。
思考p能接受后缀意味着什么:
这表明,从root到p的所有路径都是T的后缀。
1.step[q] = step[p] +1
由于到p的路径都是T的后缀,而step[q] = step[p] +1,保证了要想到q,要么经过p,要么直接从root来。
后缀自动机有一个很重要的性质,就是对于一个点w,能直接转移到它的点一定在一条pre链上。
那么如果有w能够转移到q,w所代表的也是后缀。
因此可以直接将q作为新的可以接受后缀的点。
2.step[q]>step[p]+1
此时如果直接像前面一样,不一定可行,因为可能夹杂其他字符。
怎么办呢?
可以建一个nq到x后面,使step[nq]=step[p]+1,那么就和前面的作用一样了。
即把q的son和pre都copy给nq,np、q的pre只能是nq,
最后把p的pre链上的点的子节点是q的变成nq。
这样p的pre链上的就往nq跑了,其他的就往q跑了。
复杂度分析:
空间复杂度因为每次最多加两个点,显然O(n)。
按pre边建树。
从root出发,沿着树边走到u。
u走一条非树边到v,接着走,一定能走到一个后缀。
可以说成每一条点会对应一条非树边。
所以总边数是O(n)。
注意这里是没有考虑不同字符数的常数的。
Code:
#include<cstdio>#include<string>#include<cstring>#include<iostream>#include<algorithm>#define fo(i, x, y) for(int i = x; i <= y; i ++)#define mem(a) memset(a, 0, sizeof a)#define max(a, b) ((a) > (b) ? (a) : (b))#define min(a, b) ((a) < (b) ? (a) : (b))using namespace std;const int N = 5e5 + 5;struct suffix_automation { char s[N]; int son[N][26], pre[N], step[N], last, tot; void push(int v) {step[++ tot] = v;} void Extend(int c) { push(step[last] + 1); int p = last, np = tot; for(;p && !son[p][c]; p = pre[p]) son[p][c] = np; if(!p) pre[np] = 1; else { int q = son[p][c]; if(step[q] > step[p] + 1) { push(step[p] + 1); int nq = tot; memcpy(son[nq],son[q],sizeof son[q]); pre[nq] = pre[q]; pre[q] = pre[np] = nq; for(; son[p][c] == q; p = pre[p]) son[p][c] = nq; } else pre[np] = q; } last = np; } void Build() { scanf("%s", s); tot = last = 1; mem(son); mem(pre); mem(step); for(int i = 0, E = strlen(s); i < E; i ++) Extend(s[i] - 'a'); }} suf;int main() { suf.Build();}
- 后缀自动机学习小记
- 后缀自动机学习小记
- 后缀自动机学习小记
- 【后缀自动机sam学习小记】
- 后缀自动机(SAM)学习小记
- 后缀自动机复习小记
- AC自动机学习小记
- 回文自动机学习小记
- AC自动机学习小记
- 后缀自动机学习资料
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习总结
- 后缀自动机学习资料
- 学习后缀自动机想法
- 后缀自动机学习总结
- shell中的一些后台执行命令(crontab/at/&/nohup)
- 指尖上行--移动前端开发进阶之路(读书笔记)----2.1动画形式
- 2017.12.06 source insight 代码格式化
- js 获取随机数的方法
- java基础之【继承】
- 后缀自动机学习小记
- 自适应控制---自校正PID控制器
- File类(1)——构造方法和功能实现
- struts出现Developer Notification错误时
- Linux:文件编程
- Android 反编译(一)反编译apk
- 连接CentOS下的Redis的准备工作
- 使用构造代码块精简程序
- Oracle Application Context详解(原创)