bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 对[广义后缀自动机]的一些理解
2016-06-15 07:49
204 查看
先说一下对后缀自动机的理解,主要是对构造过程的理解。
构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢?
首先,子串$[0,L+1)$对应的状态不存在,应当建立一个状态来表示这个串,显然,这个状态(np)的right集合是{L+1},max=L+1。
现在新建立了一个状态,我们还有两件事要干:找出能转移到这个状态的状态,建立链接;确定这个状态的min,即找到它在parent树上的父亲。
能转移到np的状态显然都是right集合包含L的状态,即p(子串$[0,L)$所在的状态)及p的祖先。
设c = s[L+1]我们沿着p往上爬,会遇到一些没有c的转移的状态,显然此时直接将c的转移连向它即可。
如果全都没有c的转移,那么np的父亲设为root,也就是说找到了np的min,为1。
否则,现在我们到了第一个含有c的转移的状态,此时p代表红色部分的状态。
bzoj3926
构造中,我们已经得到了前L个字符的后缀自动机,现在我们要得到L+1个字符的后缀自动机,什么需要改变呢?
首先,子串$[0,L+1)$对应的状态不存在,应当建立一个状态来表示这个串,显然,这个状态(np)的right集合是{L+1},max=L+1。
现在新建立了一个状态,我们还有两件事要干:找出能转移到这个状态的状态,建立链接;确定这个状态的min,即找到它在parent树上的父亲。
能转移到np的状态显然都是right集合包含L的状态,即p(子串$[0,L)$所在的状态)及p的祖先。
设c = s[L+1]我们沿着p往上爬,会遇到一些没有c的转移的状态,显然此时直接将c的转移连向它即可。
如果全都没有c的转移,那么np的父亲设为root,也就是说找到了np的min,为1。
否则,现在我们到了第一个含有c的转移的状态,此时p代表红色部分的状态。
#include<bits/stdc++.h> typedef long long LL; using namespace std; const int N = 100000 + 10; namespace sam { struct Node { int maxl; Node *par, *to[10]; Node() {} Node(int maxl) : maxl(maxl) {} } pool[N * 40], *pis, *root; void init() { pis = pool; root = new(pis++) Node(0); } Node *extend(Node *p, int c) { if(p->to[c]) { Node *q = p->to[c]; if(q->maxl == p->maxl + 1) return q; /*q->right++*/ Node *np = new(pis++) Node(*q); np->maxl = p->maxl + 1, q->par = np; for(; p && p->to[c] == q; p = p->par) p->to[c] = np; return np; } else { Node *np = new(pis++) Node(p->maxl + 1); for(; p && !p->to[c]; p = p->par) p->to[c] = np; if(!p) np->par = root; else { Node *q = p->to[c]; if(q->maxl == p->maxl + 1) np->par = q; else { Node *nq = new(pis++) Node(*q); nq->maxl = p->maxl + 1; q->par = np->par = nq; for(; p && p->to[c] == q; p = p->par) p->to[c] = nq; } } return np; } } LL solve() { LL res = 0; for(Node *p = pool + 1; p != pis; ++p) { res += p->maxl - p->par->maxl; } return res; } } struct Edge {int to; Edge *next;} pool[N * 2], *fir , *pis = pool; void AddEdge(int u, int v) {pis->to = v, pis->next = fir[u], fir[u] = pis++;} int ch , deg ; void dfs(int u, int fa, sam::Node *p) { sam::Node *last = sam::extend(p, ch[u]); for(Edge *p = fir[u]; p; p = p->next) { int v = p->to; if(v != fa) dfs(v, u, last); } } int main() { #ifdef DEBUG freopen("in.txt", "r", stdin); #endif int n; scanf("%d%*d", &n); for(int i = 1; i <= n; i++) scanf("%d", ch + i); for(int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); AddEdge(u, v), AddEdge(v, u); ++deg[u], ++deg[v]; } sam::init(); for(int i = 1; i <= n; i++) if(deg[i] == 1) { dfs(i, 0, sam::root); } cout << sam::solve() << endl; return 0; }
bzoj3926
相关文章推荐
- cordova platform add specified version
- my own objective of keras
- Android_eclispe的单词提示
- 面试题--HashMap详解
- AF_DataRequest详解
- poj_2506
- 依赖库
- 同步/异步 阻塞/非阻塞
- 浅谈iOS开发中方法延迟执行的几种方式
- LINUX设备驱动之platform总线
- 【MyBatis学习11】MyBatis中的延迟加载
- Lightoj 1011 - Marriage Ceremonies
- 初次接触zstack
- Linux--内核Uevent事件机制 与 Input子系统
- 关于Fragment你所需知道的一切
- Android之Activity(一):活动简介
- python常用技巧
- USB OTG原理+ ID 检测原理
- USB OTG原理+ ID 检测原理
- 《java编程思想》学习笔记(二)