您的位置:首页 > 其它

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代表红色部分的状态。

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: