说在前面
这两天写了好几道trie和AC自动机上各种瞎搞的题目,感觉自己码力+++++
1A了很开心呢,不过慢成doge….还以为会T掉hhhhh
题目
题面
Alice有
n
个字符串
接下来会发生
「1 P」,Bob往自己的集合里添加了一个字符串P。
「2 x」,Alice询问Bob,集合T中有多少个字符串包含串
Sx
。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。
输入输出格式
输入格式:
第1行,一个数n
接下来n行,每行一个字符串表示
Si
下一行,一个数q
接下来q行,每行一个操作,格式见题目描述。
输出格式:
对于每一个Alice的询问,帮Bob输出答案
解法
(直接看的题解了hhhh)
用log复杂度维护信息,再log复杂度通过信息得出答案的方式来解这道题
首先对于所有的
Si
建出AC自动机,然后把fail树搞出来。
对于一个新加进来的字符串
P
<script type="math/tex" id="MathJax-Element-920">P</script>,在AC自动机上跑一遍,每到一个节点打上一个vis标记。很明显,如果一个「末尾节点」或者「通过fail可以指向该末尾节点的节点」被vis了,那么这个末尾节点所代表的串就被覆盖了,而这些节点在fail树中都存在于末尾节点的子树内。
也就是说,现在问题转化成了「在一棵树上统计子树标记种类数」。那么对于同一种标记,将所有有当前点的标记按照dfs序排序,然后每个点的位置+1,相邻点LCA的位置-1,查询子树和即可。这一步可以用树状数组维护,正确性可以自己YY一下
(关于正确性:u,v的位置+1,那么对于LCA(u,v)上面的串相当于加了2,所以在LCA上-1。如果这时候w也要+1,但是实际上需要+1的位置只有w到LCA(w,v)这一小段,也就是在w位置+1,然后LCA(w,v)-1,同理推广)
下面是自带大常数的代码
这个代码是 真·自带大常数
/**************************************************************
Problem: 3881
User: Izumihanako
Language: C++
Result: Accepted
Time:15976 ms
Memory:437872 kb
****************************************************************/
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , Q , head[2000005] , tp , AC_cnt , endId[2000005] ;
char ss[2000005] ;
struct AC_Node ;
struct Path ;
struct AC_Node{
int isend , id ;
char c ;
AC_Node *fail , *ch[26] ;
}*root ;
struct Path{
int pre , to ;
}p[4000005] ;
struct BIT{
int b[2000005] ;
void init(){
memset( b , 0 , ( AC_cnt + 1 ) * sizeof( int ) ) ;
}
void add( int x , int delta ){
for( ; x <= AC_cnt ; x += x&-x )
b[x] += delta ;
}
int query( int x ){
int rt = 0 ;
for( ; x ; x -= x&-x ) rt += b[x] ;
return rt ;
}
}B ;
void In( int t1 , int t2 ){
p[++tp].pre = head[t1] ;
p[ head[t1] = tp ].to = t2 ;
}
int in[2000005] , out[2000005] , dfs_c ;
int fa[21][2000005] , dep[2000005] ;
void dfs( int u ){
in[u] = ++dfs_c ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
fa[0][v] = u ;
dep[v] = dep[u] + 1 ;
// printf( "(dfs )%d to %d\n" , u , v ) ;
dfs( v ) ;
}
out[u] = dfs_c ;
}
void setST(){
for( int i = 1 ; i <= 20 ; i ++ )
for( int j = 1 ; j <= AC_cnt ; j ++ )
fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
}
int Lca( int u , int v ){
if( dep[u] < dep[v] ) swap( u , v ) ;
int t = dep[u] - dep[v] , x = 0 ;
while( t ){
if( t&1 ) u = fa[x][u] ;
t >>= 1 ; x ++ ;
}
if( u == v ) return u ;
for( int i = 20 ; i >= 0 ; i -- )
if( fa[i][u] != fa[i][v] )
u = fa[i][u] , v = fa[i][v] ;
return fa[0][u] ;
}
void newNode( AC_Node *&nd ){
++ AC_cnt ;
nd = new AC_Node() ;
nd->isend = 0 ;
nd->fail = NULL ;
nd->id = AC_cnt ;
memset( nd->ch , 0 , sizeof( nd->ch ) ) ;
}
void Insert( char *ts , int idnum ){
int len = strlen( ts ) ;
AC_Node *nd = root ;
for( int i = 0 ; i < len ; i ++ ){
int nxt = ts[i] - 'a' ;
if( !nd->ch[nxt] ){
newNode( nd->ch[nxt] ) ;
nd->ch[nxt]->c = ts[i] ;
}
nd = nd->ch[nxt] ;
}
nd->isend = idnum ;
endId[idnum] = nd->id ;
}
queue<AC_Node*> que ;
void getFail(){
que.push( root ) ;
while( !que.empty() ){
AC_Node *u = que.front() ; que.pop() ;
for( int i = 0 ; i < 26 ; i ++ ){
if( !u->ch[i] ) continue ;
AC_Node *p = u->fail , *v = u->ch[i] ;
while( p && !p->ch[i] ) p = p->fail ;
v->fail = ( p ? p->ch[i] : root ) ;
que.push( v ) ;
//单向边!!!
In( v->fail->id , v->id ) ;
}
}
}
int sta[2000005] , topp ;
void Run( char *ts ){
topp = 0 ;
int len = strlen( ts ) ;
AC_Node *nd = root ;
for( int i = 0 ; i < len ; i ++ ){
int nxt = ts[i] - 'a' ;
while( nd != root && !nd->ch[nxt] )
nd = nd->fail ;
if( nd->ch[nxt] ){
nd = nd->ch[nxt] ;
sta[++topp] = nd->id ;
}
}
}
bool cmp( const int &a , const int &b ){
return in[a] < in[b] ;
}
void solve(){
getFail() ;
fa[0][1] = 1 ;
dfs( 1 ) ;
setST() ;
B.init() ;
scanf( "%d" , &Q ) ;
for( int i = 1 , opt , x ; i <= Q ; i ++ ){
scanf( "%d" , &opt ) ;
if( opt == 1 ){
scanf( "%s" , ss ) ;
Run( ss ) ;
sort( sta + 1 , sta + topp + 1 , cmp ) ;
// for( int j = 1 ; j <= topp ; j ++ )
// printf( "Passed( %d )\n" , sta[j] ) ;
for( int j = 1 ; j <= topp ; j ++ )
B.add( in[ sta[j] ] , 1 ) ;
for( int j = 2 ; j <= topp ; j ++ ){
B.add( in[ Lca(sta[j],sta[j-1]) ] , -1 ) ;
// printf( "Lca : %d\n" , Lca( sta[j] , sta[j-1] ) ) ;
}
} else{
scanf( "%d" , &x ) ;
int rg = B.query( out[ endId[x] ] ) ,
lf = B.query( in[ endId[x] ] - 1 ) ;
printf( "%d\n" , rg - lf ) ;
}
}
}
int main(){
newNode( root ) ; root->c = 0 ;
scanf( "%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%s" , ss ) ;
Insert( ss , i ) ;
}
solve() ;
}