• BZOJ 2434: [Noi2011]阿狸的打字机


    很早以前就想写这题了,但一直鸽到今天,不过对AC自动机的理解更加到位了的说

    首先我们把原串的AC自动机建出来,由于这里的删除是回退操作,因此我们记录一下每个点的父亲,遇到B就把指针移到父亲节点即可

    考虑AC自动机的性质:

    1. 在Trie树上一个点的祖先所代表的单词是当前这个点所代表的单词的前缀
    2. 一个节点的fail指针指向的字符串是当前这个点所代表的单词的最长的后缀

    然后众所周知前缀的后缀就是字串,因此我们可以得出一个暴力做法:

    对于(y)字符串所代表的每个点,在fail树上暴力向上跳,如果遇到(x)字符串的结尾就增加答案

    然后我们冷静分析一下,暴力向上肯定不好处理,假设我们把所有(y)字符串的点都打上标记,然后看看(x)字符串的子树里有多少个标记点也是可行的

    那么再进一步,问题变成怎样每次只把(y)字符串里的点打好标记呢?很简单,只要离线一下就好了

    考虑我们重新模拟建立Trie树的过程,这样我们肯定是在按序遍历所有的字符串。同时我们将询问按(y)排序,每次处理所有(y)相同的询问

    然后每次向下走的时候就把当前点点权(+1),向上走就(-1)撤销,然后相当于维护(x)的子树内的权值和

    直接DFS序+树状数组即可,总复杂度(O(26n+nlog n))

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005;
    struct ques
    {
    	int x,y,id;
    	friend inline bool operator < (const ques& A,const ques& B)
    	{
    		return A.y<B.y;
    	}
    }q[N]; int n,m,tot,cur,pos[N],ans[N],fa[N];
    struct edge
    {
    	int to,nxt;
    }e[N]; int head[N],cnt,L[N],R[N],idx; char s[N];
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    }
    class AC_Automation
    {
    	private:
    		int q[N];
    	public:
    		struct ac_node
    		{
    			int ch[26],fail;
    		}node[N];
    		#define next(x,y) node[x].ch[y]
    		#define fail(x) node[x].fail
    		inline void insert(char *s)
    		{
    			int ct=0,now=0; for (RI i=1;i<=n;++i) switch (s[i])
    			{
    				case 'P':
    					pos[++ct]=now; break;
    				case 'B':
    					now=fa[now]; break;
    				default:
    					if (!next(now,s[i]-'a')) fa[next(now,s[i]-'a')=++tot]=now;
    					now=next(now,s[i]-'a'); break;
    			}
    			
    		}
    		inline void get_fail(void)
    		{
    			RI H=0,T=0,i; for (i=0;i<26;++i) if (next(0,i)) addedge(0,q[++T]=next(0,i));
    			while (H<T)
    			{
    				int now=q[++H],to; for (i=0;i<26;++i)
    				if (!(to=next(now,i))) next(now,i)=next(fail(now),i); else
    				fail(q[++T]=to)=next(fail(now),i),addedge(fail(to),to);
    			}
    		}
    }AC;
    class Tree_Array
    {
    	private:
    		int bit[N];
    	public:
    		#define lowbit(x) (x&-x)
    		inline void add(RI x,CI y)
    		{
    			for (;x<=idx;x+=lowbit(x)) bit[x]+=y;
    		}
    		inline int get(RI x,int ret=0)
    		{
    			for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
    		}
    		#undef lowbit
    }BIT;
    inline void DFS(CI now=0)
    {
    	L[now]=++idx; for (RI i=head[now];i;i=e[i].nxt) DFS(e[i].to); R[now]=idx;
    }
    int main()
    {
    	RI i,p=1,now=0; scanf("%s",s+1); n=strlen(s+1); AC.insert(s); AC.get_fail();
    	for (scanf("%d",&m),i=1;i<=m;++i) scanf("%d%d",&q[i].x,&q[i].y),q[i].id=i;
    	for (sort(q+1,q+m+1),DFS(),i=1;i<=n;++i) switch (s[i])
    	{
    		case 'P':
    			++cur; while (q[p].y<cur&&p<=m) ++p;
    			while (q[p].y==cur&&p<=m)
    			ans[q[p].id]=BIT.get(R[pos[q[p].x]])-BIT.get(L[pos[q[p].x]]-1),++p; break;
    		case 'B':
    			BIT.add(L[now],-1); now=fa[now]; break;
    		default:
    			BIT.add(L[now=AC.node[now].ch[s[i]-'a']],1); break;
    	}
    	for (i=1;i<=m;++i) printf("%d
    ",ans[i]); return 0;
    }
    
  • 相关阅读:
    girdview
    c#中&&,||的应用
    ToString()和Convert.ToString()的区别
    日期格式化
    线程间操作ui
    基于k3cloud做的东西
    格式化金额字段添加千位符
    SQL 分页查询
    xammp 配置虚拟主机
    jQuery事件对象event的属性和方法
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12250292.html
Copyright © 2020-2023  润新知