zoukankan      html  css  js  c++  java
  • 题解 GRE Words Revenge

    题目传送门

    题目大意

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

    • 学习一个单词

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

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

    思路

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

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

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

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

    ( exttt{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] = '';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 ('
    ');
    		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:
    ",i),Solve ();
    	return 0;
    }
    
  • 相关阅读:
    如何理解c和c++ 的复杂类型声明
    xp自带扫雷bug
    求三角形的外接圆
    判断一个点是否在一个三角形内
    三角函数角度公式
    弗洛伊德(Floyd)算法
    在Win32应用程序中用Console控制台输出信息
    无法打开libcp.lib
    C#获取当前应用程序所在路径及环境变量
    C#事件的发送方和接收方(订阅方)【转】
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13457618.html
Copyright © 2011-2022 走看看