zoukankan      html  css  js  c++  java
  • Luogu P5840 【[COCI2015]Divljak】

    Description

    传送门


    Solution

    首先看到这题就想SAM对吧,然后qwaszx写了一发常数太大过不了就果断改AC自动机对吧。

    考虑对(S)建立(AC)自动机,因为字符串的所有前缀的所有后缀是字符串的所有子串,而(fail)指针指的状态就是该状态的最长可识别后缀,所以在对(S)建好(AC)自动机之后就把插入的字符串扔到(AC)自动机上跑,它能到达的所有状态就是所有的前缀。这样再对每个状态跳所有的(fail)指针就能把该字符串的所有可识别子串在(AC)自动机上找出来了。

    所以每次新插入字符串的时候只要在(fail)树上把所有经过的状态到(root)的链都加一,查询的时候查该字符串在(AC)自动机上的位置的值就行了。

    直接这样做不大好做,所以考虑树上差分,先把所有的经过的状态都找出来,然后按(dfs)序排序,每次对每个状态单点加,对相邻状态的(lca)单点减。最后查询某个点的时候只需要查询子树和即可。

    考虑为什么按照(dfs)序排序,原来的这些点是杂乱无章的,在按(dfs)序排序后就会按照从左往右的顺序依次修改,这样可以保证不重不漏。如果学过虚树的话应该很容易就能理解。


    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm> 
    #include <queue> 
    
    using namespace std;
    
    const int N = 100000;
    const int M = 2000000;
    
    int head[M + 50], num, id[N + 50], tree[M + 50], trie[M + 50][26], cnt, root, top[M + 50], maxson[M + 50], siz[M + 50], dfx[M + 50], dfxx, f[M + 50], dep[M + 50], n, tmp[M + 50], len, fail[M + 50]; 
    
    char st[M + 50];
    
    struct Node
    {
    	int next, to;
    } edge[M + 50];
    
    void Addedge(int u, int v)
    {
    	edge[++num] = (Node){head[u], v};
    	head[u] = num;
    	return;
    }
    
    void Insert(char st[M + 50], int bh)
    {
    	int l = strlen(st + 1), now = root;
    	for (int i = 1; i <= l; i++)
    	{
    		int c = st[i] - 'a';
    		if (!trie[now][c]) trie[now][c] = ++cnt;
    		now = trie[now][c]; 	
    	} 
    	id[bh] = now;
    	return;
    }
    
    void Build_AC_auto()
    {
    	queue<int> q;
    	for (int i = 0; i <= 25; i++) if (trie[root][i]) q.push(trie[root][i]);
    	while (!q.empty())	
    	{
    		int u = q.front(); q.pop();
    		for (int i = 0; i <= 25; i++)
    			if (trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i], q.push(trie[u][i]);
    			else trie[u][i] = trie[fail[u]][i];
    	}
    	return;
    }
    
    void Build_Fail_tree()
    {
    	for (int i = 1; i <= cnt; i++) Addedge(fail[i], i);
    	return;
    }
    
    void Dfs1(int x)
    {
    	dfx[x] = ++dfxx; siz[x] = 1;	
    	int maxx = 0;
    	for (int i = head[x]; i; i = edge[i].next)
    	{
    		int v = edge[i].to; f[v] = x; dep[v] = dep[x] + 1;
    		Dfs1(v);
    		siz[x] += siz[v];
    		if (siz[v] > maxx) maxx = siz[v], maxson[x] = v;
    	}
    	return;
    } 
    
    void Dfs2(int x, int topf)
    {
    	top[x] = topf;
    	if (!maxson[x]) return;
    	Dfs2(maxson[x], topf);
    	for (int i = head[x]; i; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		if (v == maxson[x]) continue;
    		Dfs2(v, v);
    	} 
    	return;
     } 
    
    int Lca(int a, int b)
    {
    	while (top[a] != top[b])
    	{
    	//	cout << a << " " << b << endl;
    		if (dep[top[a]] < dep[top[b]]) swap(a, b);
    		a = f[top[a]];	
    	}
    	return dep[a] < dep[b] ? a : b; 
    }
    
    int Lowbit(int x)
    {
    	return x & (-x);
    }
    
    void Add(int pos, int val)
    {
    	for (int i = pos; i <= cnt + 1; i += Lowbit(i)) tree[i] += val;
    	return;
    }
    
    int Query(int pos)
    {
    	int ans = 0;
    	for (int i = pos; i; i -= Lowbit(i)) ans += tree[i];
    	return ans;
    }
    
    int Cmp(int a, int b)
    {
    	return dfx[a] < dfx[b];
    }
    
    int main()
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) scanf("%s", st + 1), Insert(st, i);
    	Build_AC_auto(); Build_Fail_tree(); dep[0] = 1; Dfs1(0); Dfs2(0, 0);
    	int q;
    	scanf("%d", &q); 
    	int opt, ask;
    	while (q--)
    	{
    		scanf("%d", &opt); 
    		if (opt == 1)
    		{
    			scanf("%s", st + 1); len = 0; 
    			int now = root, l = strlen(st + 1);
    			for (int i = 1; i <= l; i++)
    			{
    				int c = st[i] - 'a';
    				now = trie[now][c];
    				tmp[++len] = now;
    			}
    			sort(tmp + 1, tmp + len + 1, Cmp);
    			len = unique(tmp + 1, tmp + len + 1) - tmp - 1;
    			for (int i = 1; i <= len; i++)
    			{
    				Add(dfx[tmp[i]], 1);
    				if (i > 1) Add(dfx[Lca(tmp[i], tmp[i - 1])], -1);
    			} 
    		}
    		else
    		{
    			scanf("%d", &ask);
    			printf("%d
    ", Query(dfx[id[ask]] + siz[id[ask]] - 1) - Query(dfx[id[ask]] - 1));
    		}
    	}
    	return 0;
    } 
    
  • 相关阅读:
    设计模式(九)外观模式Facade(结构型)
    设计模式(八)装饰器模式Decorator(结构型)
    Linux新手生存笔记[1]——Linux目录结构及说明
    设计模式(三)建造者模式Builder(创建型)
    设计模式(七)组合模式Composite(结构型)
    Linux新手生存笔记[0]——写在前面
    给出两个数m和n,求它们的最大公因子,即能够同时整出m和n的最大正整数
    Linux新手生存笔记[2]——vim训练稿
    Linux新手生存笔记[10]——shell脚本基础3函数及常用命令
    设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13097766.html
Copyright © 2011-2022 走看看