• 字符串


    题目

    Description

    Input

    Output

    题解

    显然用是AC自动机来解决

    先说一下没人写的正解
    二进制分组, 建(O(lgm))个AC自动机。 定义AC自动机的size为这个AC自动机中的字符串个数。 当两个AC自动机size相等时合并这两个AC自动机。时间复杂度(O(mlgm))

    下面是比较好想, 也比较常规的做法。
    题目中说强制在线, 但是却没有给字符串加密。 于是我们不一定要真的在线做。 我们可以用输入中出现的所有字符串建一个AC自动机, 虽然这样会把询问串也包含在内但对结果没有影响。
    每次查询还是正常的在AC自动机上走。但由于是动态修改, 我们无法像正常的AC自动机一样预处理, 于是我们需要维护fail树。
    把所有fail边拿出来建建一颗树。 然后问题转化成了: 修改一个点的权值, 询问一个点到根路径的权值和。 然后随便用数据结构维护就好了。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #include <queue>
    
    using namespace std;
    
    
    typedef long long LL;
    
    
    const int N = 1000010, M = 2000010;
    
    
    int ed[N];
    
    
    struct AC
    {
    	static const int SIZE = 1000010;
    	
    	int nxt[SIZE][26], fail[SIZE];
    	LL val[SIZE];
    	int root;
    	int sz;
    
    	AC() { }
    
    	void init()
    	{
    		sz = 0;
    		root = newnode();
    	}
    
    	int newnode()
    	{
    		memset(nxt[sz], 0, sizeof(nxt[sz]));
    		fail[sz] = 0;
    		val[sz] = 0;
    		return sz++;
    	}
    	
    	void insert(char * str, int len, int id)
    	{
    		int u = root;
    		
    		for (int i = 0; i < len; i++)
    		{
    			int c = str[i] - 'a';
    			if (!nxt[u][c])
    				nxt[u][c] = newnode();
    			u = nxt[u][c];
    		}
    		
    		ed[id] = u;
    	}
    
    	void getFail()
    	{
    		queue <int> q;
    		
    		for (int i = 0; i < 26; i++)
    			if (nxt[root][i])
    			{
    				fail[nxt[root][i]] = root;
    				q.push(nxt[root][i]);
    			}
    			else nxt[root][i] = root;
    		
    		while (!q.empty())
    		{
    			int x = q.front(); q.pop();
    			for (int i = 0; i < 26; i++)
    			{
    				if (nxt[x][i])
    				{
    					fail[nxt[x][i]] = nxt[fail[x]][i];
    					q.push(nxt[x][i]);
    				}
    				else nxt[x][i] = nxt[fail[x]][i];
    			}
    		}
    	}
    } ac;
    
    
    struct edge
    {
    	int from, to;
    	edge() { }
    	edge(int _1, int _2) : from(_1), to(_2) { }
    } edges[M];
    
    int head[N], nxt[M], tot;
    
    inline void init()
    {
    	memset(head, -1, sizeof(head));
    	tot = 0;
    }
    
    inline void add_edge(int x, int y)
    {
    	edges[tot] = edge(x, y);
    	nxt[tot] = head[x];
    	head[x] = tot++;
    	edges[tot] = edge(y, x);
    	nxt[tot] = head[y];
    	head[y] = tot++;
    }
    
    
    int dfn[N], idf[N], siz[N], dfs_clock;
    
    void dfs(int x, int fa)
    {
    	dfn[x] = ++dfs_clock;
    	idf[dfn[x]] = x;
    	siz[x] = 1;
    	
    	for (int i = head[x]; ~i; i = nxt[i])
    	{
    		edge & e = edges[i];
    		if (e.to != fa)
    		{
    			dfs(e.to, x);
    			siz[x] += siz[e.to];
    		}
    	}
    }
    
    
    struct Calc
    {
    	LL val[N];
    
    	void upd(int x, int v)
    	{
    		for (int i = x; i <= dfs_clock; i += (i & -i))
    			val[i] += v;
    	}
    
    	LL qry(int x)
    	{
    		LL Ans = 0;
    		for (int i = x; i > 0; i -= (i & -i))
    			Ans += val[i];
    		return Ans;
    	}
    } Calc;
    
    
    int m;
    
    
    int opt[N];
    
    
    char str[N];
    int L[N], R[N];
    
    
    int main()
    {
    	scanf("%d", &m);
    	
    	ac.init();
    	
    	for (int i = 1; i <= m; i++)
    	{
    		scanf("%d", &opt[i]);
    		L[i] = R[i-1] + 1;
    		scanf("%s", str + L[i]);
    		R[i] = L[i] + strlen(str + L[i]) - 1;
    		ac.insert(str + L[i], R[i] - L[i] + 1, i);
    	}
    	
    	ac.getFail();
    	
    	init();
    	
    	for (int i = 1; i < ac.sz; i++)
    		add_edge(ac.fail[i], i);
    	
    	dfs(0, -1);
    	
    	int mask = 0;
    	
    	for (int i = 1; i <= m; i++)
    	{
    		opt[i] ^= mask;
    		if (opt[i] == 1)
    		{
    			int x = ed[i];
    			int l = dfn[x], r = dfn[x] + siz[x] - 1;
    			Calc.upd(l, 1);
    			Calc.upd(r + 1, -1);
    		}
    		else if (opt[i] == 2)
    		{
    			int x = ed[i];
    			int l = dfn[x], r = dfn[x] + siz[x] - 1;
    			Calc.upd(l, -1);
    			Calc.upd(r + 1, 1);
    		}
    		else
    		{
    			LL Ans = 0;
    			int u = ac.root;
    			for (int j = L[i]; j <= R[i]; j++)
    			{
    				u = ac.nxt[u][str[j] - 'a'];
    				Ans += Calc.qry(dfn[u]);
    			}
    			mask ^= abs(Ans);
    			printf("%lld
    ", Ans);
    		}
    	}
    	
    	return 0;
    }
    

    下面是难写还慢的要命的二进制分组

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
     
    #include <queue>
     
    using namespace std;
     
     
    typedef long long LL;
     
     
    const int N = 1000010;
     
     
    char str[N]; int L[N], R[N];
     
     
    namespace mempool
    {
    	const int SIZE = 5000010;
     
    	int nxt[SIZE][26], fail[SIZE];
    	LL val[SIZE];
    	int sz;
     
    	int newNode() { return ++sz; }
    	
    	void deleteNode(int x)
    	{
    		memset(nxt[x], 0, sizeof(nxt[x]));
    		fail[x] = 0;
    		val[x] = 0;
    	}
    }
     
     
    struct AC
    {
    	vector <pair <int, int>> vec;
    	int size;
    	int root;
     
    	void remove(int u)
    	{
    		using namespace mempool;
    		if (!u) return;
    		for (int i = 0; i < 26; i++)
    			if (nxt[u][i]) remove(nxt[u][i]);
    		mempool :: deleteNode(u);
    	}
     
    	inline void clear()
    	{
    		vec.clear();
    		remove(root);
    		size = 0;
    	}
     
    	void Insert(int l, int r)
    	{
    		using namespace mempool;
    		
    		if (root == 0) { puts("error"); exit(0); }
    		
    		int u = root;
    		for (int i = l; i <= r; i++)
    		{
    			int c = str[i] - 'a';
    			if (!nxt[u][c]) nxt[u][c] = newNode();
    			u = nxt[u][c];
    		}
    		val[u]++;
    	}
     
    	void insert(int l, int r) { size++; vec.push_back(make_pair(l, r)); }
     
    	void build()
    	{
    		using namespace mempool;
    		
    		remove(root);
    		root = newNode();
    		for (auto v : vec) Insert(v.first, v.second);
    		
    		queue <int> q;
    		
    		fail[root] = root;
    		for (int i = 0; i < 26; i++)
    			if (nxt[root][i])
    			{
    				fail[nxt[root][i]] = root;
    				q.push(nxt[root][i]);
    			}
    		
    		while (!q.empty())
    		{
    			int x = q.front(); q.pop();
    			for (int i = 0; i < 26; i++)
    				if (nxt[x][i])
    				{
    					int p = fail[x];
    					while (p != root && !nxt[p][i]) p = fail[p];
    					fail[nxt[x][i]] = nxt[p][i] ? nxt[p][i] : root;
    					val[nxt[x][i]] += val[fail[nxt[x][i]]];
    					q.push(nxt[x][i]);
    				}
    		}
    	}
     
    	LL query(int l, int r)
    	{
    		using namespace mempool;
    		
    		int u = root;
    		LL Ans = 0;
    		for (int i = l; i <= r; i++)
    		{
    			int c = str[i] - 'a';
    			while (u != root && !nxt[u][c]) u = fail[u];
    			u = nxt[u][c] ? nxt[u][c] : root;
    			Ans += val[u];
    		}
    		return Ans;
    	}
    };
     
    AC add[N], del[N]; int top1, top2;
     
    void Add(int l, int r)
    {
    	add[++top1].clear();
    	add[top1].insert(l, r);
    	while (top1 > 1 && add[top1-1].size == add[top1].size)
    	{
    		for (auto v : add[top1].vec) add[top1-1].insert(v.first, v.second);
    		add[top1--].clear();
    	}
    	add[top1].build();
    }
    
    void Del(int l, int r)
    {
    	del[++top2].clear();
    	del[top2].insert(l, r);
    	while (top2 > 1 && del[top2-1].size == del[top2].size)
    	{
    		for (auto v : del[top2].vec) del[top2-1].insert(v.first, v.second);
    		del[top2--].clear();
    	}
    	del[top2].build();
    }
     
    LL Qry(int l, int r)
    {
    	LL Ans = 0;
    	for (int i = 1; i <= top1; i++)
    		Ans += add[i].query(l, r);
    	for (int i = 1; i <= top2; i++)
    		Ans -= del[i].query(l, r);
    	return Ans;
    }
    
    
    int n;
     
     
    int main()
    {
    	scanf("%d", &n);
    	
    	LL mask = 0;
    	
    	for (int i = 1; i <= n; i++)
    	{
    		int opt;
    		scanf("%d", &opt);
    		opt ^= mask;
    		scanf("%s", str + R[i-1] + 1);
    		L[i] = R[i-1] + 1;
    		R[i] = L[i] + strlen(str + L[i]) - 1;
    		
    		if (opt == 1) Add(L[i], R[i]);
    		else if (opt == 2) Del(L[i], R[i]);
    		else
    		{
    			LL Ans = Qry(L[i], R[i]);
    			mask ^= abs(Ans);
    			printf("%lld
    ", Ans);
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    位运算与变量多状态表达
    判断点是否在多边形内
    向量旋转
    小怪受击身体变红特效代码
    字典 Key值转换为数组
    Android中的Selector的使用总结
    Android 常见的工具类
    成为Android高手必须掌握的8项基本要求
    K-means算法
    Android 5.0以上获取系统运行进程信息
  • 原文地址:https://www.cnblogs.com/2016gdgzoi509/p/11313066.html
Copyright © 2020-2023  润新知