1. 程式人生 > 實用技巧 >一篇文章帶你瞭解CSS基礎知識和基本用法

一篇文章帶你瞭解CSS基礎知識和基本用法

題目連結

Solution [NOI2011]阿狸的打字機

題目大意:給定一顆\(Trie\)樹,多次詢問\(x\)號節點代表的字串在\(y\)號節點代表的字串中出現了多少次

AC自動機


分析:首先我們回顧一下\(AC\)自動機上匹配的過程

我們沿著文字串在\(Trie\)圖上面走(假設已經用類似路徑壓縮的辦法,把\(Trie\)樹給補全,當然此題直接從根走到\(y\)即可),走到每個節點,一直沿著\(fail\)跳,跳到的節點代表能匹配(當前節點代表字首的字尾)的字首(有點繞加了個括號)。

跳到的節點如果是某個字串的結尾,那麼就匹配上了。

那麼這題的暴力演算法就是從根走到\(y\),路徑上每個節點跳\(fail\)

,走到\(x\)結尾就把答案加\(1\)

顯然複雜度無法承受,我們考慮從\(x\)的角度來考慮

\(fail\)邊反向就可以得到\(fail\)樹,實際上我們只需要查詢\(x\)為根的子樹內,有多少個節點在根到\(y\)的鏈

可以線上做,也可以離線,利用\(dfs\)序來統計

我們將詢問\((x,y)\)儲存在\(y\)內,進行一次\(dfs\),在新進入一個節點時在它\(dfs\)序位置\(+1\),離開時撤銷。對於它所有的詢問,直接查詢子樹和即可

樹狀陣列可以維護

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 1e5 + 100;
struct mpair{int first,second;};
inline mpair make_pair(int x,int y){return mpair{x,y};}
extern int ans[maxn];
namespace bit{
	int f[maxn];
	inline int lowbit(int x){return x & (-x);}
	inline void add(int pos,int x){
		while(pos < maxn){
			f[pos] += x;
			pos += lowbit(pos);
		}
	}
	inline int query(int pos){
		int res = 0;
		while(pos){
			res += f[pos];
			pos -= lowbit(pos);	
		}
		return res;
	}
	inline int query(int a,int b){return query(b) - query(a - 1);}
}
namespace graph{
	vector<int> G[maxn];
	inline void addedge(int from,int to){G[from].push_back(to);}
	int siz[maxn],dfn[maxn],dfs_tot;
	inline void dfs(int u){
		dfn[u] = ++dfs_tot;
		siz[u] = 1;
		for(int v : G[u])dfs(v),siz[u] += siz[v];
	}
}
namespace ac{
	const int siz = 26;
	int ch[maxn][siz],chhis[maxn][siz],faz[maxn],fail[maxn],tot;
	vector<mpair> query[maxn]; 
	inline int idx(char c){return c - 'a';}
	inline void build(){
		memcpy(chhis,ch,sizeof(ch)); 
		queue<int> Q;
		for(int c = 0;c < siz;c++)
			if(ch[0][c])Q.push(ch[0][c]);
		while(!Q.empty()){
			int u = Q.front();Q.pop();
			for(int c = 0;c < siz;c++)
				if(ch[u][c])fail[ch[u][c]] = ch[fail[u]][c],Q.push(ch[u][c]);
				else ch[u][c] = ch[fail[u]][c];
		}
		for(int u = 1;u <= tot;u++)graph::addedge(fail[u],u);
	}
	inline void dfs(int u){
//		printf("%d\n",u);
		if(u)bit::add(graph::dfn[u],1);
		if(u)for(auto p : query[u]){
//			printf("")
			ans[p.first] = bit::query(graph::dfn[p.second],graph::dfn[p.second] + graph::siz[p.second] - 1);
		}
		for(int c = 0;c < siz;c++)
			if(chhis[u][c])dfs(chhis[u][c]);
		if(u)bit::add(graph::dfn[u],-1);
	}
}
char str[maxn];
int m,now,pos[maxn],ans[maxn],print;
int main(){
#ifdef LOCAL
	freopen("type3.in","r",stdin);
#endif
	scanf("%s",str + 1);
	for(int i = 1;str[i];i++){
		if(str[i] == 'B')now = ac::faz[now];
		else if(str[i] == 'P')pos[++print] = now;
		else{
			int c = ac::idx(str[i]);
			if(!ac::ch[now][c])ac::ch[now][c] = ++ac::tot,ac::faz[ac::tot] = now;
			now = ac::ch[now][c];
		}
	}
	ac::build();
	graph::dfs(0);
	scanf("%d",&m);
	for(int x,y,i = 1;i <= m;i++){
		scanf("%d %d",&x,&y);
		ac::query[pos[y]].push_back(make_pair(i,pos[x]));
	}
	ac::dfs(0);
	for(int i = 1;i <= m;i++)printf("%d\n",ans[i]);
	return 0;
}