BZOJ 2434: [Noi2011]阿狸的打字机【AC自动机,fail树.dfs序,树状数组

80 篇文章 0 订阅
4 篇文章 0 订阅

……我……嗯……建trie写成了n^2,T了一年

然后奇技淫巧艹到rank1了……坐等被yigezhongxuesheng大爷艹回来233333


这题……本来觉得后面那一堆会调一年,然而万万没想到是跪在了建trie上


询问x串在y串中出现过多少次,等价于查询在【以AC自动机的根节点为根】的fail树上,x串终止节点的子树中出现过多少个y串中字符对应的节点

于是用树状数组维护fail树的DFS序,遍历trie树(也就是给出的字符串),并将当前在stack中的节点的值++,当访问到终止态的时候,查询当前终止态代表的字符串上挂的询问,直接用树状数组求个区间和就好


代码………………我觉得不能看2333333

flaze先去写一波备注哼

…………

#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;	int n;
int N;
//=========================================================
//=====FIO=======FTC大爷的读入输出优化==== 
struct buf{
    char z[1<<25],*s;
    char e[1<<25],*t;
    char f[24];
    buf():s(z),t(e){
        fread(z,1,sizeof z,stdin);
    }
    ~buf(){
        fwrite(e,1,t-e,stdout);
    }
    void read_str(char* v){
    	while(*s<48)++s;
    	while(*s>32)*v++=*s++;
    	*v=0;
	}
    char* str(){
        while(*s<48)++s;
        char* v=s;
        while(*s>32)++s;
        *s=0;
        return v;
    }
    
    void out(const char* v){
        while(*v)*t++=*v++;
        *t++=10;
    }
    operator int(){
        int v=0,j=0;
        while(*s<48)j=*s++;
        while(*s>32)
            v=v*10+*s++-48;
        return j^45?v:-v;
    }
    void out(int v){
        char* l=f;
        if(!v)*t++=48;
        else{
            if(v<0){*t++=45;
                v*=-1;}
            for(;v;v/=10)
                *l++=v%10+48;
            while(l-f)
                *t++=*--l;
        }
        *t++=10;
    }
}it;

//==========================================================
//=====Query Linker=======
//====离线,用链表记录每个字符串有哪些查询===== 
struct MISSION{
	int id,x,nxt;
}mis[MAXN];	int cnt_mis; 
int mis_head[MAXN];
void add_mis(int id,int x,int y){
	mis[++cnt_mis].x = x;
	mis[cnt_mis].id = id;
	mis[cnt_mis].nxt = mis_head[y];
	mis_head[y] = cnt_mis;
}

int ans[MAXN];
//=========================================================
//====BIT========
//===用树状数组维护dfs序上的区间和===== 
struct BIT{
	int a[MAXN];
	int sum[MAXN];
	inline void add(int x,int v){
		a[x] += v;
		for(int i = x;i<=N;i+=i&-i)	sum[i] += v;
	}
	inline int inqry(int x,int y){
		int rtn = 0;
		for(int i=y;i;i-=i&-i)
			rtn += sum[i];
		for(int i=x-1;i;i-=i&-i)
			rtn-=sum[i];
		return rtn;
	}
}Tree_Array;

//====================================================
//========Fail Tree==========
//=====fail树,搞dfs序,记录子树所在的区间=== 
struct EDGE{
	int to,nxt;
}edge[MAXN];	int cnt_edge;
int fst[MAXN];
void addedge(int x,int y){
	edge[++cnt_edge].to = y;
	edge[cnt_edge].nxt = fst[x];
	fst[x] = cnt_edge;
}

//==子树区间的左端点,右端点======= 
int dfn_b[MAXN],dfn_e[MAXN],cnt_dfs;
void dfs(int now){
	dfn_b[now] = ++cnt_dfs;
	for(int tmp = fst[now];tmp;tmp=edge[tmp].nxt){
		int aim = edge[tmp].to;
		dfs(aim);
	}
	dfn_e[now] = cnt_dfs;
}

//==============================================================
//====AC Automaton======
struct ACAM{
	int son[MAXN][26],fail[MAXN];
	int root,cnt_node;
	inline void init(){root = cnt_node = 1;}

	int que[MAXN],head,tail;
	inline void bfs(){
		head = tail = 0;
		int now = root;
		for(int x=0;x<26;++x){
			if(son[now][x])	que[tail++] = son[now][x];
			(son[now][x]?fail[son[now][x]]:son[now][x]) = now;
		}
		while(head^tail){
			now = que[head++];
			addedge(fail[now],now);
			for(int x=0;x<26;++x){
				if(son[now][x])	que[tail++] = son[now][x];
				(son[now][x]?
					fail[son[now][x]] : son[now][x]) = son[ fail[now] ][x];
			}
		}
	}
}YJQ;


char s[MAXN];//读入的字符串 
int id2[MAXN],cnt_id2;
//id2[i]:编号为i的字符串在trie树上的终止节点
int iid2[MAXN];
//iid2[i]:i号节点对应的字符串 
int s_id[MAXN];
//s_id[i]: 读入字符串中的第i个字符对应的节点标号 

int pre[MAXN];	//trie树上的父节点 
inline void YJQ_insert(){
	int now = YJQ.root;
	int lth = strlen(s);
	for(int i=0;i<lth;++i){
		char x = s[i];
		if(x == 'P')
			id2[++cnt_id2] = now , iid2[now] = cnt_id2;
		if(x == 'B')	now = pre[now] ;
		if(x>='a'&&x<='z'){
			int k = x-'a';
			if(!YJQ.son[now][k])	YJQ.son[now][k] = ++YJQ.cnt_node;
			pre[YJQ.son[now][k]] = now , now = YJQ.son[now][k];
			s_id[i] = now;
		}
	}
}

inline void prep(){
	it.read_str(s);
	YJQ_insert();
	YJQ.bfs();
}
//============================================================
//===Main====

int stk[MAXN],top;	//在最后遍历trie树时的当前字符串 

int main(){
	YJQ.init();
	prep();
	N = YJQ.cnt_node;
	n=it;
	for(int i=1;i<=n;++i){
		int u=it,v=it;
		add_mis(i,u,v);
	}
	dfs(YJQ.root);//QAQQQQ
	int Len = strlen(s);

	for(int i=0;i<Len;++i){
		char x = s[i];
		if(x>='a'&&x<='z')	Tree_Array.add(dfn_b[s_id[i]],1) , stk[++top] = s_id[i];
		if(x=='B')	Tree_Array.add(dfn_b[stk[top]],-1) , --top;
		if(x=='P'){
			int now = iid2[stk[top]];
			for(int tmp = mis_head[now];tmp;tmp=mis[tmp].nxt){
				int aim = id2[mis[tmp].x];
				ans[mis[tmp].id] = Tree_Array.inqry(dfn_b[aim],dfn_e[aim]);
			}
		}
	}
	for(int i=1;i<=n;++i)
		it.out(ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值