zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 97 G. Death DBMS题解

    传送门

    前言

    这是我第一次在cf比赛时做出AC自动机的题,而且是在比赛最后30秒AC的,特发博客纪念一波。

    题意


    中文简单说一下就是:有n个字符串,初始值为0。有两种操作,操作1是将序号为i的字符串的值赋为x,操作2是找到所有是字符串t的子串的字符串的值的最大值。

    题解

    多串匹配问题很显然是个AC自动机问题,如果这题没有两个相同的串就几乎是一道模板题,只要在建trie时记录每个串对应的节点,修改时直接修改对应节点即可,查询时遍历后缀链接找出最大值即可。但如果有多个串相同,可能同一节点会有多个串与之对应,如果我们还是用val[ ]存储每个节点对应串的最大值,就要在每次修改中找到该点对应的所有串中的最大值(所以我们对每个节点建立一棵线段树)。我的做法是,对每个节点开一个pair型的优先队列,第一关键字存储数值,第二关键字存储对应字符编号,每次修改在对应节点加入pair(x,i),再根据该节点的优先队列值修改其val值。具体地,取出当前节点优先队列的首元素,如果首元素的i值已经不对应x值了,就pop出去,重复这一过程,否则x值为所有对应串的最大值。因为每次修改最多入队一次、出队一次,所以总修改时间复杂度为(O(Qlog Q))。而查询操作与普通AC自动机一样,于是总时间复杂度为(O(N+Qlog Q))

    代码

    /*************************************************************************
    	> File Name: 2.cpp
    	> Author: Knowledge_llz
    	> Mail: 925538513@qq.com 
    	> Blog: https: https://www.cnblogs.com/Knowledge-Pig/ 
    	> Created Time: 2020/10/26 23:11:01
     ************************************************************************/
    
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define LL long long
    #define pb push_back
    #define fi first
    #define se second
    #define pr pair<int,int>
    #define mk(a,b) make_pair(a,b)
    using namespace std;
    int read(){
    	char x=getchar(); int u=0,fg=0;
    	while(!isdigit(x)){ if(x=='-') fg=1; x=getchar(); }
    	while(isdigit(x)){ u=(u<<3)+(u<<1)+(x^48); x=getchar(); }
    	return fg?-u:u;
    }
    const int maxx=1e6+10;
    char s[maxx],t[maxx];
    int n,m,pos[maxx],sp[maxx];
    priority_queue<pr>que[maxx];
    struct Trie{
    	int ch[maxx][30],val[maxx],f[maxx],last[maxx],sz;
    	Trie(){ sz=1; memset(ch[0],0,sizeof(ch[0])); }
    	int idx(char c){ return c-'a'; }
    	void insert(char *T,int id){
    		int u=0,len=strlen(T);
    		for(int i=0;i<len;++i){
    			int c=idx(T[i]);
    			if(!ch[u][c]){
    				memset(ch[sz],0,sizeof(ch[sz]));
    				val[sz]=-1;
    				ch[u][c]=sz++;
    			}
    			u=ch[u][c];
    		}
    		val[u]=0;
    		pos[id]=u;
    		que[u].push(pr(0,id));
    	}
    	void getfail(){				
    		queue<int>q;		
    		f[0]=0;
    		For(c,0,25){
    			int u=ch[0][c];
    			if(u){
    				q.push(u);
    				f[u]=0;
    			}
    		}
    		while(!q.empty()){
    			int r=q.front(); q.pop();
    			For(c,0,25){
    				int u=ch[r][c];
    				if(!u)  ch[r][c]=ch[f[r]][c];
    				else{
    					q.push(u);
    					f[u]=ch[f[r]][c];
    					last[u]=(val[f[u]]>=0)?f[u]:last[f[u]];
    				}
    			}
    		}
    	}
    	int Find(char *T){			
    		int len=strlen(T);
    		int j=0,ans=-1;
    		For(i,0,len-1){
    			int c=idx(T[i]);
    			j=ch[j][c];
    			for(int x=j;x;x=last[x]){
    				ans=max(val[x],ans);
    			}
    		}
    		return ans;
    	}
    }trie;
    
    
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("input.in", "r", stdin);
    	freopen("output.out", "w", stdout);
    #endif
    	n=read(); m=read();
    	For(i,1,n){
    		scanf("%s",t);
    		trie.insert(t,i);
    	}
    	trie.getfail();
    	while(m--){
    		int o=read();
    		if(o==1){
    			int u=read(),v=read(),z=pos[u];
    			sp[u]=v;
    			que[z].push(pr(v,u));
    			while(!que[z].empty()){
    				int x=que[z].top().fi,y=que[z].top().se;
    				if(sp[y]==x) break;
    				else que[z].pop();
    			}
    			trie.val[z]=que[z].top().fi;
    		}
    		else{
    			scanf("%s",t);
    			printf("%d
    ",trie.Find(t));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    sublime-生成html1.0
    sublime代码片段
    单色半透明-兼容IE7
    IE
    身心被掏空
    屏幕适配的方法
    宫格布局实例(注意jquery的版本号要统一)
    宫格布局实例(注意jquery的版本号要统一)2
    有 n个整数,使其前面各数顺序向后移 m 个位置,最后m个数变成最前面的 m 个数。
    给出两个&#160;非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照&#160;逆序&#160;的方式存储的,并且它们的每个节点只能存储&#160;一位&#160;数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
  • 原文地址:https://www.cnblogs.com/Knowledge-Pig/p/13901303.html
Copyright © 2011-2022 走看看