Live2D

题解 GRE Words Revenge

题目传送门

题目大意

给出 \(m\) 次操作,分别为以下两种操作:

  • 学习一个单词

  • 给出一个段落,查询里面有多少个学过的单词。注意,如果学习过 \(\text{ab,bc}\) ,当前查询段落为 \(\text{abc}\) ,那么应该算 \(2\) 个单词。

\(m\le 10^5\),保证学习的单词长度之和 \(\le 10^5\),给出的段落长度之和 \(\le 5\times 10^6\)

思路

据说可以直接拿暴力艹过去。。。(不会吧?阿sir?

可以看出来,如果只有一次查询,那其实就是一个裸的AC自动机,于是,问题就变成了如何维护一个动态的AC自动机。但是显然我们不可以,于是问题就是如何搞出一个伪在线AC自动机。

我们发现我们可以开两个AC自动机S1,S2,当S2里面的点数 \(\ge \sqrt n\) 的时候我们就直接把S1,S2进行暴力合并,否则暴力重构S2,就有点像根号分治。

然后我们通过分析发现我们的时间复杂度其实是 \(\Theta(n\sqrt n+m)\) 的,其中 \(n\) 是AC自动机里面的点数。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 5000005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct ACAM{
	bool flag;//表示是否需要建立失配指针。 
	int tot,ch[MAXN][2],cnt[MAXN],fail[MAXN],last[MAXN];//fail表示失配指针,last表示上次有贡献的点,0是根,cnt表示是否是结束节点 
	void clear (){
		for (Int i = 0;i <= tot;++ i) ch[i][0] = ch[i][1] = cnt[i] = 0;
		tot = 0,flag  =0;
	} 
	void buildfail (){
		queue <int> q;
		for (Int i = 0;i < 2;++ i){
			if (!ch[0][i]) continue;
			q.push (ch[0][i]),fail[ch[0][i]] = last[ch[0][i]] = 0;
		}
		while (!q.empty()){
			int u = q.front();q.pop ();
			for (Int i = 0;i < 2;++ i){
				int v = ch[u][i];
				if (!v) continue;
				int p = fail[u];while (p && !ch[p][i]) p = fail[p];
				fail[v] = ch[p][i],last[v] = cnt[fail[v]] ? fail[v] : last[fail[v]];
				q.push (v);  
			}
		}
	}
	void insert (char *s){
		flag = 1;int x = 0;
		for (Int i = 0;s[i];++ i){
			int now = s[i] - '0';
			if (!ch[x][now]) ch[x][now] = ++ tot;
			x = ch[x][now];
		}
		cnt[x] = 1;
	}
	int calc (int u){
		int res = 0;
		while (u){
			res += cnt[u];
			u = last[u];
		}
		return res;
	}
	int match (char *s){
		if (flag) buildfail (),flag = 0;
		int res = 0,x = 0;
		for (Int i = 0;s[i];++ i){
			int now = s[i] - '0';
			while (x && !ch[x][now]) x = fail[x];
			x = ch[x][now];
			res += calc (x);
		}
		return res;
	}
	void Merge (ACAM &ot,int u,int v){
		flag = 1;
		for (Int i = 0;i < 2;++ i){
			if (!ot.ch[v][i]) continue;
			if (!ch[u][i]) ch[u][i] = ++ tot;
			Merge (ot,ch[u][i],ot.ch[v][i]);
		} 
		cnt[u] |= ot.cnt[v];
	}
}SM[2];

char s[MAXN],t[MAXN];

void Solve (){
	int n;read (n);
	SM[0].clear(),SM[1].clear();
	int lastans = 0,up = sqrt (MAXN);
	while (n --> 0){
		scanf ("%s",s);
		int len = strlen (s + 1),shift = lastans % len;
		t[len] = '\0';for (Int i = 0;i < len;++ i) t[i] = s[(i + shift) % len + 1];
		if (s[0] == '?') write (lastans = SM[0].match (t) + SM[1].match (t)),putchar ('\n');
		else{
			SM[1].insert (t);
			if (SM[1].tot > up) SM[0].Merge (SM[1],0,0),SM[1].clear();  
		}
	}
}

signed main(){
	int t;read (t);
	for (Int i = 1;i <= t;++ i) printf ("Case #%d:\n",i),Solve ();
	return 0;
}
posted @ 2020-08-08 14:13  Dark_Romance  阅读(170)  评论(0编辑  收藏  举报