題目描述數組
輸入ui
輸出spa
樣例輸入blog
3
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3排序
樣例輸出字符串
1
2
1it
題解io
AC自動機+樹鏈的並+DFS序+樹狀數組class
P的子串體現爲前綴的後綴,某個前綴的全部後綴在AC自動機上體現爲:Trie樹上該前綴對應節點的fail樹到根節點的鏈上節點。queue
所以對全部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; }