zoukankan      html  css  js  c++  java
  • CodeForces 547E Mike and Friends AC自动机 主席树

    题意:

    给出(n)个字符串(s_i)(q)个询问:

    • (l,r,k)(sumlimits_{i=l}^{r}count(i, k)),其中(count(i,j))表示(s_j)作为子串在(s_i)中出现的次数

    分析:

    先不考虑查询中(l,r)的限制,考虑该字符串(s_k)在一个字符串集合中出现的次数。
    先将这个字符串集合插入到一棵Trie数中,并且每经过一个节点就将其对应的(val)值加(1)
    这样(s_k)对应节点的(val)值就是以(s_k)前缀的字符串的个数。
    然而这还不够,接着构造出一棵fail树,也就是AC自动机中的fail指针构成的树。
    这棵fail树中,父节点是子节点的后缀
    因为fail树中(s_k)是其子节点的后缀,所以再加上子树节点的(val)值。
    这样就把问题转化为了求子树节点的权值之和:

    利用DFS序将子树转为区间,然后用线段树维护区间和

    然后考虑上题中的(l,r),可以将线段树可持久化或者离线查询加树状数组维护,而且离线应该是很快的。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <set>
    #include <queue>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    #define PB push_back
    #define PII pair<int, int>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define PER(i, a, b) for(int i = b - 1; i >= a; i--)
    #define ALL(x) x.begin(), x.end()
    
    const int maxn = 200000 + 10;
    const int nlogn = maxn * 40;
    
    //persistant segment tree
    int lch[nlogn], rch[nlogn], sum[nlogn];
    int tot, root[maxn], p[maxn];
    
    void update(int& rt, int pre, int L, int R, int p, int v = 1) {
    	rt = ++tot;
    	if(L == R) { sum[rt] = sum[pre] + v; return; }
    	int M = (L + R) / 2;
    	if(p <= M) { rch[rt] = rch[pre]; update(lch[rt], lch[pre], L, M, p, v); }
    	else { lch[rt] = lch[pre]; update(rch[rt], rch[pre], M+1, R, p, v); }
    	sum[rt] = sum[lch[rt]] + sum[rch[rt]];
    }
    
    int qL, qR;
    int query(int rt, int pre, int L, int R) {
    	int ans = 0;
    	if(qL <= L && R <= qR) return sum[rt] - sum[pre];
    	int M = (L + R) / 2;
    	if(qL <= M) ans += query(lch[rt], lch[pre], L, M);
    	if(qR > M) ans += query(rch[rt], rch[pre], M+1, R);
    	return ans;
    }
    
    //fail tree
    vector<int> G[maxn];
    int l[maxn], r[maxn], dfs_clock;
    
    void dfs(int u) {
    	l[u] = ++dfs_clock;
    	for(int v : G[u]) dfs(v);
    	r[u] = dfs_clock;
    }
    
    //Trie
    int sz;
    int ch[maxn][26], f[maxn], pos[maxn], fa[maxn];
    
    void insert(int id, char* s) {
    	int u = 0;
    	for(int i = 0; s[i]; i++) {
    		int c = s[i] - 'a';
    		if(!ch[u][c]) {
    			ch[u][c] = ++sz;
    			memset(ch[sz], 0, sizeof(ch[0]));
    		}
    		fa[ch[u][c]] = u;
    		u = ch[u][c];
    	}
    	pos[id] = u;
    }
    
    void getFail() {
    	queue<int> Q;
    	REP(c, 0, 26) if(ch[0][c]) {
    		Q.push(ch[0][c]);
    		G[0].PB(ch[0][c]);
    	}
    	while(!Q.empty()) {
    		int r = Q.front(); Q.pop();
    		REP(c, 0, 26) {
    			int u = ch[r][c];
    			if(!u) ch[r][c] = ch[f[r]][c];
    			else {
    				f[u] = ch[f[r]][c];
    				G[f[u]].PB(u);
    				Q.push(u);
    			}
    		}
    	}
    }
    
    int n, q;
    char s[maxn];
    
    int main() {
    	scanf("%d%d", &n, &q);
    	REP(i, 0, n) {
    		scanf("%s", s);
    		insert(i + 1, s);
    	}
    	getFail();
    	dfs(0);
    	int cur = 0, s;
    	REP(i, 1, n + 1) {
    		for(int j = pos[i]; j; j = fa[j]) {
    			cur++;
    			update(root[cur], root[cur-1], 1, dfs_clock, l[j]);
    		}
    		p[i] = root[cur];
    	}
    
    	while(q--) {
    		int x, y, k; scanf("%d%d%d", &x, &y, &k);
    		qL = l[pos[k]], qR = r[pos[k]];
    		printf("%d
    ", query(p[y], p[x-1], 1, dfs_clock));
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    Redis操作命令大全
    Redis实用监控工具一览
    Redis缓存雪崩、缓存穿透、缓存击穿、缓存降级、缓存预热、缓存更新
    Redis GEO地理位置信息,查看附近的人
    详解redis持久化
    详解Supervisor进程守护监控
    详解Redis Cluster集群
    arduino使用rfid
    树莓派控制WS2812
    Arduino读取温湿度dh11+烟雾气体MQ2+彩灯ws2812
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/7994362.html
Copyright © 2011-2022 走看看