题解 CF1207G Indie Album

\(\Large\natural\) CF1207G Indie Album \ 原题链接

解法

这种题算是比较套路的了,我们看见这种多字符串匹配,会想到AC自动机

如果不会AC自动机的可以看看 我的博客 ,希望能给您带来帮助。

那么如果我们对于所有字典树节点连边 \(i-fail_i\)(根节点没有 \(fail\),不连边),那么就得到了一棵 Fail树

那么,如果一号点为根节点,那所有 \(fail\) 直接或间接指向 \(i\) 号点的节点,都在 \(i\) 的子树中。

所以查询字符串 \(X\) 在字符串 \(Y\) 中出现几次,等价于建出 TrieFail树 后,在 Fail树中 以 “\(X\)的结束节点”(设为 \(i\)) 为根的子树中有多少个 \(Y\) 包含的节点。

不理解可以看这解释:比如 \(j\) 号点是 \(Y\) 所包含的,是 \(Y\) 的第 \(id\) 个节点,那么代表在 \(Y\) 查询时,\(j\)\(fail\) 可以跑出 \(X\),所以 \(X\)\(Y\)\(1\sim id\) 这个子串的后缀。

对于这道题,我们可以类比 阿机的打字狸

设询问的字符串为 \(T\),则显然有这样一个方法:让 \(T\)Trie 上跑,经过的点对于的 Fail树节点权值加一。然后我们查询 \(S\)Trie 的结尾节点 \(G\),对应的 Fail树节点 ,的子树和(可能有点绕)。最后我们再把那些加一的点再减一,复原原来的树。

把答案离线后,我们可以在 Trie树DFS,来到一个点,这个点对于的 Fail树节点 权值加一;离开一个点,同样地权值减一。这样当我们到达某个字符串的末尾时,我们可以保证有且仅有这个字符串的点上有值。然后我们调出在这个点上的每一个询问,查询它们对应的 Fail树节点 的子树和即可。

所以我们需要支持单点加值,以及查询一个点的子树和,可以用 DFS序 解决。

因为一个点的子树在 DFS序 上是连续的,所以我们只用区间查询(给子树查询)、单点加值( 在 Trie 跑上 DFS 时加减)就行了,显然套树状数组模板即可。

代码

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define mar(o) for(int E=fst[o];E;E=e[E].nxt)
#define v e[E].to
using namespace std;
const int n7=401234,m7=801234,z7=1601234;
struct dino{int to,nxt;}e[z7];
struct lyca{int z,id;};
int n,T,cnt=1,tre[m7][26],fail[m7],poi[n7],head,tail,que[m7];
int ecnt,fst[m7],t,atre[n7],L[n7],R[n7],ans[n7];
char cr[n7];bool tru[m7][26];
vector <lyca> vec[m7];

int rd(){
   int shu=0;char ch=getchar();
   while(!isdigit(ch))ch=getchar();
   while(isdigit(ch))shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
   return shu;
}

void edge(int sta,int edn){
	ecnt++;
	e[ecnt]=(dino){edn,fst[sta]};
	fst[sta]=ecnt;
}

void insert1(){
	rep(i,1,n){
		int sys=rd(),las=1;
		if(sys==2)las=poi[ rd() ];
		char ch=getchar()-'a';
		if(!tre[las][ch]){
			cnt++,tre[las][ch]=cnt;
		}
		poi[i]=tre[las][ch];
	}
}

void insert2(int z){
	int len=strlen(cr+1),now=1;
	rep(i,1,len){
		int ch=cr[i]-'a';
		if(!tre[now][ch]){
			cnt++,tre[now][ch]=cnt;
		}
		now=tre[now][ch];
	}
	poi[z]=now;
}

void Gfail(){
	head=1,tail=1,que[1]=1;
	rep(i,0,25)tre[0][i]=1;
	while(head<=tail){
		int now=que[head];
		rep(i,0,25){
			int edn=tre[ fail[now] ][i];
			if(tre[now][i]){
				fail[ tre[now][i] ]=edn;
				edge(edn,tre[now][i]);
				tail++,que[tail]=tre[now][i];
				tru[now][i]=1;
			}
			else tre[now][i]=edn;
		}
		head++;
	}
}

#define lb(z) (z&-z)
void updat(int z,int id){
	while(id<=cnt)atre[id]+=z,id+=lb(id);
}
int Dquery(int id){
	int tot=0;
	while(id)tot+=atre[id],id-=lb(id);
	return tot;
}
int query(int l,int r){
	return Dquery(r)-Dquery(l-1);
}

void dfs1(int o){
	t++,L[o]=t;
	mar(o)dfs1(v);
	R[o]=t;
}

int fimd(){
	int len=strlen(cr+1),now=1;
	rep(i,1,len)now=tre[now][ cr[i]-'a' ];
	return now;
}

void dfs2(int o){
	updat(1,L[o]);
	int wal=vec[o].size()-1;
	rep(i,0,wal){
		int ll=L[ vec[o][i].z ];
		int rr=R[ vec[o][i].z ];
		ans[ vec[o][i].id ]=query(ll,rr);
	}
	rep(i,0,25){
		if(tru[o][i])dfs2(tre[o][i]); 
	}
	updat(-1,L[o]);
}

int main(){
	n=rd(),insert1(),T=rd();
	rep(i,1,T){
		int z=rd();scanf("%s",cr+1);
		insert2(i+n);
		vec[ poi[z] ].push_back( (lyca){poi[i+n],i} );
	}
	Gfail(),dfs1(1),dfs2(1);
	rep(i,1,T)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2021-02-04 22:03  BlankAo  阅读(70)  评论(0编辑  收藏  举报