• BZOJ 2434 Luogu P2414 [NOI2011]阿狸的打字机 (AC自动机、树状数组)


    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2434

    题解:

    我写的是离线做法,不知道有没有在线做法。

    转化一波题意,(x)在AC自动机上代表的字符串在(y)代表的字符串中出现的次数等于(x)在fail树的子树内有多少个点是(y)点Trie树上的祖先。

    然后不难得到做法: 将询问离线按照(y)在AC自动机上的id排序(一个正常的构建Trie的方式建出来应该就满足节点的编号是它的Trie树DFS序).

    在Trie树上DFS, 同时回答询问, 用树状数组维护即可

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 1e5;
    const int S = 26;
    struct Edge
    {
    	int v,nxt;
    } e[(N<<1)+3];
    struct Query
    {
    	int x,y,id;
    	bool operator <(const Query &arg) const
    	{
    		return y<arg.y;
    	}
    } qr[N+3];
    int fe[N+3];
    int dfn[N+3];
    int fa[N+3];
    int son[N+3][S+3];
    int fail[N+3];
    char str[N+3];
    int stk[N+3];
    int id[N+3];
    int que[N+3];
    int bit[N+3];
    int ord[N+3];
    int ans[N+3];
    int sz[N+3];
    int n,q,siz,m,cnt,en;
    
    void addval(int lrb,int val)
    {
    	while(lrb<=cnt)
    	{
    		bit[lrb] += val;
    		lrb += (lrb&(-lrb));
    	}
    }
    
    int querysum(int rb)
    {
    	int ret = 0;
    	while(rb)
    	{
    		ret += bit[rb];
    		rb -= (rb&(-rb));
    	}
    	return ret;
    }
    
    void addedge(int u,int v)
    {
    	en++; e[en].v = v;
    	e[en].nxt = fe[u]; fe[u] = en;
    }
    
    void buildTrie()
    {
    	int u = 0,tp = 0; stk[tp] = 0;
    	for(int i=1; i<=n; i++)
    	{
    		if(str[i]=='B')
    		{
    			stk[tp] = 0; tp--;
    			u = stk[tp];
    		}
    		else if(str[i]=='P')
    		{
    			m++; id[m] = u;
    		}
    		else
    		{
    			str[i]-=96;
    			if(!son[u][str[i]]) {siz++; son[u][str[i]] = siz;}
    			u = son[u][str[i]];
    			tp++; stk[tp] = u;
    		}
    	}
    }
    
    void buildACA()
    {
    	int head = 1,tail = 0;
    	for(int i=1; i<=S; i++) {if(son[0][i]) {tail++; que[tail] = son[0][i];} fail[son[0][i]] = 0;}
    	while(head<=tail)
    	{
    		int u = que[head]; head++;
    		addedge(fail[u],u); addedge(u,fail[u]);
    		for(int i=1; i<=S; i++)
    		{
    			if(son[u][i]) {fail[son[u][i]] = son[fail[u]][i]; tail++; que[tail] = son[u][i];}
    			else {son[u][i] = son[fail[u]][i];}
    		}
    	}
    }
    
    void dfs(int u)
    {
    	cnt++; dfn[u] = cnt;
    	sz[u] = 1;
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		if(e[i].v==fa[u]) continue;
    		fa[e[i].v] = u;
    		dfs(e[i].v);
    		sz[u] += sz[e[i].v];
    	}
    }
    
    int main()
    {
    	scanf("%s",str+1); n = strlen(str+1);
    	scanf("%d",&q);
    	buildTrie();
    	buildACA();
    	for(int i=1; i<=q; i++)
    	{
    		int x,y; scanf("%d%d",&x,&y);
    		qr[i].x = id[x]; qr[i].y = id[y]; qr[i].id = i;
    	}
    	dfs(0);
    	sort(qr+1,qr+q+1);
    	int u = 0,tp = 0,j = 0; stk[tp] = 0; addval(dfn[u],0);
    	for(int i=1; i<=n; i++)
    	{
    		if(str[i]=='B')
    		{
    			addval(dfn[u],-1);
    			stk[tp] = 0; tp--;
    			u = stk[tp];
    		}
    		else if(str[i]=='P')
    		{
    			while(j<q && qr[j+1].y==u)
    			{
    				j++;
    				ans[qr[j].id] = querysum(dfn[qr[j].x]+sz[qr[j].x]-1)-querysum(dfn[qr[j].x]-1);
    			}
    		}
    		else
    		{
    			u = son[u][str[i]];
    			tp++; stk[tp] = u;
    			addval(dfn[u],1);
    		}
    	}
    	for(int i=1; i<=q; i++)
    	{
    		printf("%d
    ",ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Python学习Day1
    Linux使用外部邮箱发送邮件
    Linux命令学习1(awk、grep、sed)
    html笔记之表格
    html笔记之认识标签
    zabbix笔记之告警配置
    zabbix笔记之zabbix基础知识了解
    Windows之80端口被系统占用
    python笔记之流程控制
    python笔记之基本数据类型
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11057129.html
Copyright © 2020-2023  润新知