spoj cot4 bzoj 2787 Count on a trie

题意:
维护两个字符串集合S,T,一开始S和T都只有一个空串,编号都为1,要求支持操作:
1.在S的某一个串Si后添加一个字符c,加入S
2.在T的某一个串Ti的前面或后面添加一个字符c,加入T
3.将T的两个串Ti,Tj首尾相接形成一个新串TiTj,加入T
4.询问T中的某个串Ti在S中某个串Si中的出现次数.(如果Ti是空串,输出0)


题解:
离线,对S建立后缀自动机,获得S反串的后缀树及后缀数组。
对T的一个串ti对应了后缀数组上满足LCP >= |ti|的一个区间,这个区间也对应了后缀自动机上一个节点,通过haxi映射一下。
对于操作3:(logn)
ti和tj合并的时候通过二分获得新的区间(因后缀数组是反串的,实际是tj和si合并)。
设ti的区间是[Li, Ri], tj是[Lj, Rj], 新串是[L, R];
则[L, R]属于[Li, Ri],二分L时判断后缀s[L+|ti|]与tj的大小(比较是O(1)),而s[L+|ti|]是s[L]在trie树上第|ti|个祖先,需要有O(1)求第k祖先的算法:ladder+倍增。二分R同理。
对于操作2:O(1)
后面添加是后缀自动机上走一步, 前面添加是后缀树上走一步或者不走
对于询问再最后dfs一下
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
         
           #include 
           #include 
           
             #include 
            
              #include 
             
               using namespace std; #define N 300010 #define M 2000010 #define ULL unsigned long long #define LL long long #define ls (i << 1) #define rs (ls | 1) #define md ((ll + rr) >> 1) #define lson ll, md, ls #define rson md + 1, rr, rs #define inf 0x3f3f3f3f #define eps 1e-4 #define pii pair 
              
                #define MP make_pair #define mod 1000000007 int sa[N], rank[N], sa_cnt; struct Query{ int ty, x, y, c; Query() {} Query(int ty, int i, int j, int c) : ty(ty), x(i), y(j), c(c) {}; }que[N]; int q_cnt; int lab_s[N], s_cnt; int t_cnt; int ans[N]; int ll[N], rr[N]; struct node { int id, len; node () {} node (int id, int len) : id(id), len(len) {}; }nd[N]; int nd_cnt; struct Hash { LL state[N]; int fst[N<<1], nxt[N], val[N], e; int n; void init(int sz) { n = sz; memset(fst, -1, sizeof fst); e = 0; } LL hx(int l, int r) { return (LL) l * n + r; } void insert(int l, int r, int v) { LL st = hx(l, r); int u = st % N; val[e] = v; state[e] = st; nxt[e] = fst[u]; fst[u] = e++; } int query(int l, int r) { LL st = hx(l, r); int u = st % N; for(int i = fst[u] ;~i; i = nxt[i]) if(state[i] == st) return val[i]; return 0; } }hm; namespace trie { int ch[N][26], tot; int fa[N][22]; int dep[N], h[N], bel[N]; int pos[N]; //pos in ladder int lab[N]; // id in sam int bit[N]; // tree array int letter[N], len[N]; vector 
               
                 ladder[N]; vector 
                
                  query[N]; int cnt; // number of ladder void init() { cnt = 0; tot = 1; fa[1][0] = 0; len[1] = 0, letter[1] = -1; memset(ch[1], 0, sizeof ch[1]); } int insert(int cur, int c) { if(ch[cur][c]) return ch[cur][c]; int k = ++tot; fa[k][0] = cur; len[k] = len[cur] + 1; letter[k] = c; return ch[cur][c] = k; } void dfs(int u, int d) { dep[u] = d; h[u] = 1; int id = -1, mx = -1; for(int i = 0; i < 26; ++i) if(ch[u][i]) { int v = ch[u][i]; dfs(v, d+1); if(h[v] > mx) mx = h[v], id = bel[v]; } if(id == -1) { ++cnt; ladder[cnt].clear(); ladder[cnt].push_back(u); pos[u] = 0; bel[u] = cnt; } else { pos[u] = ladder[id].size(); ladder[id].push_back(u); h[u] = mx + 1; bel[u] = id; } } void build() { for(int i = 1; i <= 20; ++i) for(int j = 1; j <= tot; ++j) fa[j][i] = fa[fa[j][i-1]][i-1]; dfs(1, 0); for(int i = 1; i <= cnt; ++i) { int sz = ladder[i].size(); int u = ladder[i][sz-1]; u = fa[u][0]; for(int j = 0; j < sz && u; ++j, u = fa[u][0]) ladder[i].push_back(u); } /* printf("trie::build\n"); for(int i = 1; i <= tot; ++i) printf("bel[%d] : %d, fa %d\n", i, bel[i], fa[i][0]); for(int i = 1; i <= cnt; ++i) { printf("i %d : ", i); for(int j = 0; j < ladder[i].size(); ++j) printf("%d ", ladder[i][j]); puts(""); } puts(""); */ } int get(int u, int d) { if(d == 0) return u; if(d > dep[u]) return 0; int k = floor(log(d+0.0)/log(2.0)); u = fa[u][k]; d -= 1< 
                 
                   0) ans[id] = getsum(rr[nd[v].id]) - getsum(ll[nd[v].id]-1); else ans[id] = 0; } for(int i = 0; i < 26; ++i) if(ch[u][i]) getans(ch[u][i]); add(rank[u], -1); } } namespace sam { int par[N], son[N][26]; int val[N]; int root; int tot; int go[N][26]; // suffix tree int lab[N]; //id in trie int id[N], num[N]; //sort int right[N]; // one in right set void clear(int x) { par[x] = 0; val[x] = 0; lab[x] = 0; } void init() { tot = 0; sa_cnt = 0; clear(1); root = ++tot; } void copy(int nq, int q) { memcpy(son[nq], son[q], sizeof son[q]); par[nq] = par[q]; } int extend(int last, int w) { int np = ++tot; int p = last; clear(np); val[np] = val[p] + 1; while(p && son[p][w] == 0) son[p][w] = np, p = par[p]; if(p == 0) par[np] = root; else { int q = son[p][w]; if(val[q] == val[p]+ 1) par[np] = q; else { int nq = ++tot; copy(nq, q); val[nq] = val[p] + 1; par[np] = par[q] = nq; while(p && son[p][w] == q) son[p][w] = nq, p = par[p]; } } return np; } void mark(int sid, int tid) { right[sid] = lab[sid] = tid; for(int i = 0; i < 26; ++i) if(trie::ch[tid][i]) mark(son[sid][i],trie::ch[tid][i]); } void dfs(int u) { if(lab[u]) sa[++sa_cnt] = lab[u], rank[lab[u]] = sa_cnt; for(int i = 0; i < 26; ++i) if(go[u][i]) dfs(go[u][i]); } void build() { init(); queue 
                  
                    que; que.push(1); //(trie node) trie::lab[1] = 1; while(!que.empty()) { int x = que.front(); que.pop(); for(int i = 0; i < 26; ++i) if(trie::ch[x][i] ) { int v = trie::ch[x][i]; trie::lab[v] = extend(trie::lab[x], i); que.push(v); } } mark(1, 1); for(int i = 0; i <= tot; ++i) num[i] = 0; for(int i = 1; i <= tot; ++i) ++num[val[i]]; for(int i = 1; i <= tot; ++i) num[i] += num[i-1]; for(int i = 1; i <= tot; ++i) id[num[val[i]]--] = i; for(int j = tot; j > 0; --j) { int v = id[j], u = par[v]; right[u] = right[v]; //the label of leaf on the path to root in parent tree int tmp = trie::get(right[u], val[u]); go[u][trie::letter[tmp]] = v; } dfs(1); for(int i = 1; i <= tot; ++i) if(lab[i]) ll[i] = rr[i] = rank[lab[i]]; else ll[i] = inf, rr[i] = -1; for(int j = tot; j > 0; --j) { int v = id[j], u = par[v]; ll[u] = min(ll[u], ll[v]); rr[u] = max(rr[u], rr[v]); } hm.init(trie::tot+1); for(int i = 1; i <= tot; ++i) hm.insert(ll[i], rr[i], i); /* printf("sa_cnt %d\n", sa_cnt); for(int i = 1; i <= sa_cnt; ++i) printf("sa[%d]: %d\n", i, sa[i]); */ } } int cmp(int a, node b) { a = rank[a]; int l = ll[b.id], r = rr[b.id]; if(a < l) return -1; else if(a > r) return 1; else return 0; } node add0(node a, int c) { if(!a.id) return node(0,0); int id = a.id; if(sam::son[id][c] == 0) return node(0, 0); id = sam::son[id][c]; return node(id, a.len+1); } node add1(node a, int c) { int id = a.id; if(!a.id) return node(0,0); if(a.len == sam::val[id]) { if(!sam::go[id][c]) return node(0, 0); return node(sam::go[id][c], a.len+1); } else { int tmp = trie::get(sam::right[id], a.len); if(trie::letter[tmp] == c) return node(id, a.len+1); else return node(0, 0); } } node merge(node a, node b) { if(!a.id || !b.id) return node(0, 0); int l = ll[a.id], r = rr[a.id]+1; int _l, _r; while(l < r) { int mid = l + r >> 1; int f = trie::get(sa[mid], a.len); if(f==0 || cmp(f, b) < 0) l = mid+1; else r = mid; } _l = l; l = ll[a.id], r = rr[a.id]+1; while(l < r) { int mid = l + r >> 1; int f = trie::get(sa[mid], a.len); if(f && cmp(f, b) > 0) r = mid; else l = mid+1; } _r = l-1; return node(hm.query(_l, _r), a.len+b.len); } int get_int() { char c; while((c=getchar()) && !('0' <= c && c <= '9')); int ans = c-'0'; while((c=getchar()) && '0' <= c && c <= '9') ans = ans * 10 + c-'0'; return ans; } char get_char() { char c; while((c=getchar()) && !('a' <= c && c <= 'z')); return c; } int main() { trie::init(); lab_s[1] = 1; s_cnt = 1, t_cnt = 1; q_cnt = 0; int m; m = get_int(); for(int i = 1; i <= m; ++i) { int ty, x, y; char c; ty = get_int(); x = get_int(); if(ty == 1) { c = get_char(); ++s_cnt; lab_s[s_cnt] = trie::insert(lab_s[x], c-'a'); } else if(ty == 2) { y = get_int(); c = get_char(); ++q_cnt; que[q_cnt] = Query(x+1, y, 0, c-'a'); } else { y = get_int(); que[++q_cnt] = Query(ty, x, y, 0); } } trie::build(); sam::build(); nd_cnt = 1; nd[1] = node(1, 0); int ret = 0; for(int i = 1; i <= q_cnt; ++i) { if(que[i].ty == 4) { int id = lab_s[que[i].y]; ++ret; trie::query[id].push_back(MP(ret, que[i].x)); } else { ++t_cnt; if(que[i].ty == 1) nd[t_cnt] = add1(nd[que[i].x], que[i].c); else if(que[i].ty == 2) nd[t_cnt] = add0(nd[que[i].x], que[i].c); else nd[t_cnt] = merge(nd[que[i].y], nd[que[i].x]); } } trie::getans(1); for(int i = 1; i <= ret; ++i) printf("%d\n", ans[i]); }  
                   
                  
                 
                
               
              
             
            
          
        
       
       
      
      
     
     
    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值