【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(n\log 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\n" , query(lp[pos[x]]) - query(vp[pos[x]] - 1));
	}
	return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章