zoukankan      html  css  js  c++  java
  • Codeforces 163E(ac自动机、树状数组)

    要点

    • 显然ac自动机的板子就可以暴力一下答案了
    • 为了优化时间复杂度,考虑套路fail树的dfs序。发现本题需要当前这个尾点加上所有祖先点的个数,考虑使用树状数组差分一下,在父点+1,在子树后-1,每次询问前缀和即可
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <queue>
    using namespace std;
    
    typedef long long ll;
    const int N = 1e6 + 5, maxk = 1e5 + 5;
    
    int n, k, pos[maxk], in[maxk];
    string s, t;
    
    struct FenwickTree {
    	ll F[N];
    
    	void Modify(int x, int val) {
    		for (; x < N; x += x&-x)
    			F[x] += val;
    	}
    
    	ll Query(int x) {
    		ll res = 0;
    		for (; x; x -= x&-x)
    			res += F[x];
    		return res;
    	}
    }bit;
    
    struct Aho_Corasick_Automaton {
        int ch[N][26];//Trie树的转移
        //int val[N];//根据题意赋值。有值则意味着某子串末尾
        int fail[N];//失配,转移到别的树枝接着找
        int sz;//注意这个板子sz一定是要从1开计
        vector<int> adj[N];//用于fail树
        int dfn[N], Time, size[N];
    
        void insert(string s, int id) {//Trie的插入
            int len = s.length(), now = 0;
    
            for (int i = 0; i < len; i++){
                int c = s[i] - 'a';
                if (!ch[now][c]) {
                    sz++;
                    memset(ch[sz], 0, sizeof ch[sz]);
                    //val[sz] = 0;
                    ch[now][c] = sz;
                }
                now = ch[now][c];
            }
            //val[now] = id;
            pos[id] = now;
        }
    
        void getfail(){
            queue<int> Q;
            for (int i = 0; i < 26; i++)
                if (ch[0][i]) {
                	adj[0].emplace_back(ch[0][i]);
                    fail[ch[0][i]] = 0, Q.push(ch[0][i]);//第二层指向根
                }
            while (!Q.empty()){
                int u = Q.front(); Q.pop();
                for (int i = 0; i < 26; i++)
                    if (ch[u][i]) {
                    	adj[ch[fail[u]][i]].emplace_back(ch[u][i]);
                        fail[ch[u][i]] = ch[fail[u]][i], Q.push(ch[u][i]);//指向其他枝上同样的字母
                    }
                    else ch[u][i] = ch[fail[u]][i];//使得find时半路突然失配时还能一下拐回去
            }
        }
    
        void dfs(int u) {
        	dfn[u] = ++Time;
        	size[u] = 1;
        	for (int v : adj[u]) {
        		dfs(v);
        		size[u] += size[v];
        	}
        }
    
        int find(string T){
            int len = T.length(), now = 0;
            ll res = 0;
    
            for (int i = 0; i < len; i++){
                now = ch[now][T[i] - 'a'];
                res += bit.Query(dfn[now]);
                // for (int t = now; t; t = fail[t])//事实上这是一个不断缩短后缀的过程
                //     if (val[t]) res++;
            }
            return res;
        }
    }ac;
    
    int calc(string t) {
    	stringstream ss(t);
    	int x; ss >> x;
    	return x;
    }
    
    int main() {
    	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    	cin >> n >> k;
    	for (int i = 1; i <= k; i++) {
    		cin >> s;
    		ac.insert(s, i);
    		in[i] = 1;
    	}
    	ac.getfail();
    	ac.dfs(0);
    	for (int i = 1; i <= k; i++) {
    		int id = pos[i];
    		bit.Modify(ac.dfn[id], 1);
    		bit.Modify(ac.dfn[id] + ac.size[id], -1);
    	}
    
    	for (int i = 0; i < n; i++) {
    		cin >> t;
    		char x = t[0]; t.erase(0, 1);
    		if (x == '+') {
    			int v = calc(t);
    			if (!in[v]) {
    				int id = pos[v];
    				bit.Modify(ac.dfn[id], 1);
    				bit.Modify(ac.dfn[id] + ac.size[id], -1);
    				in[v] = 1;
    			}
    		} else if (x == '-') {
    			int v = calc(t);
    			if (in[v]) {
    				int id = pos[v];
    				bit.Modify(ac.dfn[id], -1);
    				bit.Modify(ac.dfn[id] + ac.size[id], 1);
    				in[v] = 0;
    			}
    		} else {
    			cout << ac.find(t) << '
    ';
    		}
    	}
    }
    
  • 相关阅读:
    在VS2005中 GridView导入Excel的两点小技巧
    ASP.NET页面事件:顺序与回传详解
    .NET泛型编程简介
    关于ASP.NET在IIS一些问题的经验总结
    ASP.NET生成静态页面实现方法
    ASP.NET 2.0防止同一用户同时登陆
    【经验总结】简陋无比的观察者模式实现
    javascript变量作用域一点总结
    javascript中"/"运算符常见错误
    【经验总结】构造函数的强制调用
  • 原文地址:https://www.cnblogs.com/AlphaWA/p/11006283.html
Copyright © 2011-2022 走看看