zoukankan      html  css  js  c++  java
  • 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组

    题目描述

    Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的。
    接下来会发生q个操作,操作有两种形式:
    “1 P”,Bob往自己的集合里添加了一个字符串P。
    “2 x”,Alice询问Bob,集合T中有多少个字符串包含串S_x。(我们称串A包含串B,当且仅当B是A的子串)
    Bob遇到了困难,需要你的帮助。

    输入

    第1行,一个数n;
    接下来n行,每行一个字符串表示S_i;
    下一行,一个数q;
    接下来q行,每行一个操作,格式见题目描述。

    输出

    对于每一个Alice的询问,帮Bob输出答案。

    样例输入

    3
    a
    bc
    abc
    5
    1 abca
    2 1
    1 bca
    2 2
    2 3

    样例输出

    1
    2
    1


    题解

    AC自动机+树链的并+DFS序+树状数组

    P的子串体现为前缀的后缀,某个前缀的所有后缀在AC自动机上体现为:Trie树上该前缀对应节点的fail树到根节点的链上节点。

    因此对所有S串建立AC自动机,求出fail树,那么添加一个P串,它所包含的S串的范围就是P在Trie树上每个位置(P的每个前缀)fail树上到根节点所覆盖的所有节点,即把树链的并+1。

    把所有位置按照DFS序排序,每个点到根节点路径上+1,每相邻两点LCA到根节点路径上-1。查询就是查单点权值。需要支持:到根节点的路径加、单点求值,差分后变为单点加、子树求值,使用DFS将子树转化为区间,再用树状数组维护区间和即可。

    时间复杂度 $O(nlog n)$ 。

    #include <queue>
    #include <cstdio>
    #include <algorithm>
    #define N 100010
    #define M 2000010
    using namespace std;
    queue<int> q;
    int c[M][26] , fail[M] , tot = 1 , pos[N] , head[M] , to[M] , next[M] , cnt , fa[M][20] , deep[M] , log[M] , vp[M] , lp[M] , tp , f[M] , val[M] , tv;
    char str[M];
    void build()
    {
    	int x , i;
    	for(i = 0 ; i < 26 ; i ++ ) c[0][i] = 1;
    	q.push(1);
    	while(!q.empty())
    	{
    		x = q.front() , q.pop();
    		for(i = 0 ; i < 26 ; i ++ )
    		{
    			if(c[x][i]) fail[c[x][i]] = c[fail[x]][i] , q.push(c[x][i]);
    			else c[x][i] = c[fail[x]][i];
    		}
    	}
    }
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x)
    {
    	int i;
    	vp[x] = ++tp;
    	for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    	for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
    	lp[x] = tp;
    }
    inline int lca(int x , int y)
    {
    	int i;
    	if(deep[x] < deep[y]) swap(x , y);
    	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
    		if(deep[x] - deep[y] >= (1 << i))
    			x = fa[x][i];
    	if(x == y) return x;
    	for(i = log[deep[x]] ; ~i ; i -- )
    		if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
    			x = fa[x][i] , y = fa[y][i];
    	return fa[x][0];
    }
    inline void fix(int x , int a)
    {
    	int i;
    	for(i = x ; i <= tp ; i += i & -i) f[i] += a;
    }
    inline int query(int x)
    {
    	int i , ans = 0;
    	for(i = x ; i ; i -= i & -i) ans += f[i];
    	return ans;
    }
    bool cmp(int a , int b)
    {
    	return vp[a] < vp[b];
    }
    int main()
    {
    	int n , m , i , j , t , opt , x;
    	scanf("%d" , &n);
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		scanf("%s" , str) , t = 1;
    		for(j = 0 ; str[j] ; j ++ )
    		{
    			if(!c[t][str[j] - 'a']) c[t][str[j] - 'a'] = ++tot;
    			t = c[t][str[j] - 'a'];
    		}
    		pos[i] = t;
    	}
    	build();
    	for(i = 2 ; i <= tot ; i ++ ) add(fail[i] , i) , log[i] = log[i >> 1] + 1;
    	dfs(1);
    	scanf("%d" , &m);
    	while(m -- )
    	{
    		scanf("%d" , &opt);
    		if(opt == 1)
    		{
    			scanf("%s" , str) , t = 1 , tv = 0;
    			for(i = 0 ; str[i] ; i ++ ) t = c[t][str[i] - 'a'] , val[++tv] = t;
    			sort(val + 1 , val + tv + 1 , cmp);
    			for(i = 1 ; i <= tv ; i ++ ) fix(vp[val[i]] , 1);
    			for(i = 1 ; i < tv ; i ++ ) fix(vp[lca(val[i] , val[i + 1])] , -1);
    		}
    		else scanf("%d" , &x) , printf("%d
    " , query(lp[pos[x]]) - query(vp[pos[x]] - 1));
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    处理数据
    Vue--Vue常用指令及操作
    Vue--Vue实例
    ES6--解构赋值
    ES6--入门
    前端模块化开发
    Node.js--入门
    HTML5--canvas与svg的使用
    input整理
    移动端开发入门
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8715901.html
Copyright © 2011-2022 走看看