Codeforces 710F

Description

维护一个集合支持:

$1.$ 添加一个串。

$2.$ 删除一个串。

$3.$ 给出一个串,询问有多少个子串在集合中出现过,多次出现多次计算。

Solution

若没有强制在线就直接离线打标记 $\text{AC}$ 自动机即可。

但是这题需要强制在线。

考虑最暴力的算法,即为建立两个 $\text{AC}$ 自动机,将加入的串和删除的串分别丢进去,每次暴力重构,最后直接跑匹配答案相减即可。

显然这种做法的时间复杂度是非常不科学的,思考时间复杂度到底在哪里爆炸了,发现是多次暴力重构的问题。

考虑是否有一种方案能够以较少的暴力重构次数来维护这个问题,那么我们想到了二进制分组。

对于每 $2^k$ 个串维护一个 $\text{AC}$ 自动机,这样即维护了一个 $\text{AC}$ 自动机的栈。

这样最多共有 $\log$ 个 $\text{AC}$ 自动机,插入时如果当前 $\text{AC}$ 自动机中串的个数和前个数相同,那么合并两个自动机。每个字符串只会被合并 $\log$ 次,每次合并时暴力重构即可。

Code

//Dlove's template
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>

#define R register
#define ll long long
#define ull unsigned long long
#define db double
#define ld long double
#define sqr(_x) ((_x) * (_x))
#define Cmax(_a, _b) ((_a) < (_b) ? (_a) = (_b), 1 : 0)
#define Cmin(_a, _b) ((_a) > (_b) ? (_a) = (_b), 1 : 0)
#define Max(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define Min(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define Abs(_x) (_x < 0 ? (-(_x)) : (_x))

using namespace std;

namespace Dntcry
{
	inline int read()
	{
		R int a = 0, b = 1; R char c = getchar();
		for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
		for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
		return a * b;
	}
	inline ll lread()
	{
		R ll a = 0, b = 1; R char c = getchar();
		for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
		for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
		return a * b;
	}
	const int Maxn = 300010;
	int m, len;
	char S[Maxn];
	struct node
	{
		int key, tim;
		node *ch[26], *ac[26], *fail;
	}pool[Maxn << 2], *tot = pool, *q[Maxn << 2];
	struct ACM
	{
		node *rt[30];
		int cnt, siz[30];
		void Insert(R node *root)
		{
			R node *x = root;
			for(R int i = 1; i <= len; i++)
			{
				R int opt = S[i] - 'a';	
				if(!x->ch[opt]) x->ch[opt] = ++tot;
				x = x->ch[opt];
			}
			x->key = 1;
		}
		void Build(R node *root)
		{
			R int head = 0, tail = 0;
			root->tim = 0, root->fail = root;
			q[++tail] = root;
		    for(R int i = 0; i < 26; i++) root->ac[i] = root;
		    while(head < tail)
			{
		        R node *x = q[++head];
		        x->tim = x->fail->tim + x->key;
		        for(R int i = 0; i < 26; i++)
		            if(x->ch[i])
					{
		            	q[++tail] = x->ch[i];
		              	x->ch[i]->fail = x->fail->ac[i]; 
		                x->ac[i] = x->ch[i];
					}
		            else x->ac[i] = x->fail->ac[i];
			}
		}
		node *Merge(R node *x, R node *y)
		{
			if(!x) return y;
			if(!y) return x;
			x->key += y->key;
			for(R int i = 0; i < 26; i++) 
				x->ch[i] = Merge(x->ch[i], y->ch[i]);
       		return x;
		}
		void Make()
		{
			rt[++cnt] = ++tot;
			Insert(rt[cnt]);
			siz[cnt] = 1;
			while(cnt > 1 && siz[cnt] == siz[cnt - 1])
			{
            	rt[cnt - 1] = Merge(rt[cnt - 1], rt[cnt]);
            	//puts("merged");
            	siz[cnt - 1] += siz[cnt];
            	siz[cnt--] = 0;
			}
			Build(rt[cnt]);
		}
		int Query()
		{
		    R int res = 0;
		    for(R int x = 1; x <= cnt; x++)
			{
		    	R node *now = rt[x];
		        for(R int i = 1; i <= len; i++)
				{
		        	now = now->ac[S[i] - 'a'];
		        	res += now->tim;
				}
			}
		    return res;
		}
	}In, Out;
	int k, last;
	int Main()
	{
		scanf("%d%d", &m, &k);
		while(m--)
		{
			R int opt;
			scanf("%d%s", &opt, S + 1);
			len = strlen(S + 1);
			if(k) for(R int i = 1; i <= len; i++) S[i] = 'a' + (S[i] - 'a' + last) % 26;
			switch(opt)
			{
				case 1: In.Make(); break;
				case 2: Out.Make(); break;
				case 3: last = In.Query() - Out.Query(); printf("%d\n", last); break;
			}
		}
		return 0;
	}
}
int main()
{
	return Dntcry :: Main();
}

  

猜你喜欢

转载自www.cnblogs.com/DntcryBecthlev/p/10448284.html