[AC自動機] AC自動機 從基礎到進階-unfinished

T1 Censoring G

Censoring S一樣
考慮用棧

#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e5+10;
char s[NNN],t[NNN];
int n,sz;
struct Trie{
	int s[26];
	int val,fail,last;
}ch[NNN];
int sta[NNN],top;
int rt[NNN];//記錄棧內元素對應trie裏的節點 

inline void update(){
	int p=0,len=strlen(t+1);
	for(int i=1;i<=len;++i){
		int c=t[i]-'a';
		if(!ch[p].s[c]) ch[p].s[c]=++sz;
		p=ch[p].s[c];
	}
	ch[p].val=len;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<=26;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
	}
	
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
			ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
		}
	}
}

inline void query(){
	int ans=0,len=strlen(s+1),p=0;
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		p=ch[p].s[c];
		rt[i]=p;
		sta[++top]=i;
		if(ch[p].val){
			top-=ch[p].val;
			if(!top) p=0;
			else p=rt[sta[top]];
		}
	}
}

int main(){
	scanf("%s",s+1);
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",t+1);
		update();
	}
	get_fail();
	query();
	for(int i=1;i<=top;++i)
		putchar(s[sta[i]]);
	return 0;
}

那個

  if(!top) p=0;
  else p=rt[sta[top]];

加不加無所謂,加要快一點

T2 Word

多次搜索肯定炸
考慮把文章拼在一塊,單詞之間用奇怪的字符分開

注意判重

#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e7+10;
int n,sz,lt;
char s[NNN],t[NNN];
struct Trie{
	int s[26];
	int fail,last,id;
}ch[NNN];
int ans[NNN],same[NNN];

inline void update(int id){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'a';
		if(!ch[p].s[c]) ch[p].s[c]=++sz;
		p=ch[p].s[c];
		t[++lt]=s[i];
	}
	if(ch[p].id) same[id]=ch[p].id;
	else ch[p].id=id;
	t[++lt]='#';
	return;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
			ch[p].last=ch[ch[p].fail].id?ch[p].fail:ch[ch[p].fail].last;
		}
	}
}

inline void query(){
	int p=0;
	for(int i=1;i<=lt;++i){
		int c=t[i]-'a';
		p=ch[p].s[c];
		int tmp=0;
		if(ch[p].id) tmp=p;
		else if(ch[p].last) tmp=ch[p].last;
		while(tmp){
			++ans[ch[tmp].id];
			tmp=ch[tmp].last;
		}
	}
}

int main(){
	n=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		update(i);
	}
	get_fail();
	query();
	for(int i=1;i<=n;++i){
		if(!same[i])printf("%d\n",ans[i]);
		else printf("%d\n",ans[same[i]]);
	}
	return 0;
}

T3 Video Game G

ACMDP
常見套路:設dp[i][j]dp[i][j]表示長度爲i,末節點在ACM上節點j的最佳答案
dp[i][son[p]]=max{dp[i1][p]+val[son[p]]} dp[i][son[p]]=\max\{dp[i-1][p]+val[son[p]]\}
注意fail樹上下放val
注意枚舉的範圍,從根節點開始

#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e3+10;
const int INF=1e9;
int n,k,sz,res;
char s[NNN];
struct Trie{
	int s[3];
	int val,fail,last;
}ch[NNN];
int dp[NNN][NNN];

inline void update(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		int c=s[i]-'A';
		if(!ch[p].s[c]) ch[p].s[c]=++sz;
		p=ch[p].s[c];
	}
	++ch[p].val;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<3;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
		ch[p].fail=ch[p].last=0;
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<3;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
			ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
		}
		ch[u].val+=ch[ch[u].fail].val;
	}
}

inline void query(){
	for(int i=0;i<=k;++i)
		for(int j=0;j<=sz;++j)
			dp[i][j]=-INF;
	dp[0][0]=0;
	for(int len=1;len<=k;++len)
		for(int p=0;p<=sz;++p)
			for(int j=0;j<3;++j)
				dp[len][ch[p].s[j]]=max(dp[len][ch[p].s[j]],dp[len-1][p]+ch[ch[p].s[j]].val);
}

int main(){
	n=in,k=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		update();
	}
	get_fail();
	query();
	for(int i=1;i<=sz;++i)
		res=max(res,dp[k][i]);
	printf("%d\n",res);
	return 0;
}

T4 VIDEO - Video game combos

跟T3沒什麼區別,雙倍經驗吧(複製粘貼打法好)

T5 文本生成器

考慮ACMDP
dp[i][j][0/1]dp[i][j][0/1]表示文章長ii,末節點爲jj,不可讀或可讀的文章
易知pson[p]p\to son[p]每一個字母都會覆蓋到
考慮轉移

  • 對於單詞末節點
    dp[len][son[p]][1]=dp[len1][p][0]+dp[len1][p][1] dp[len][son[p]][1]=\sum dp[len-1][p][0]+dp[len-1][p][1]
  • 對於其他節點
    dp[len][son[p]][0]=dp[len1][p][0]dp[len][son[p]][1]=dp[len1][p][1] dp[len][son[p]][0]=\sum dp[len-1][p][0]\\ dp[len][son[p]][1]=\sum dp[len-1][p][1]
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=2e4+10;
const int MOD=1e4+7;
int n,m,sz;
char s[NNN];
struct Trie{
	int s[26];
	int fail,last,val;
}ch[NNN];
int dp[200][NNN][2],ans;

inline void update(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		if(!ch[p].s[s[i]-'A']) ch[p].s[s[i]-'A']=++sz;
		p=ch[p].s[s[i]-'A'];
	}
	ch[p].val=1;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
			ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
		}
		ch[u].val|=ch[ch[u].fail].val;
	}
}

inline void query(){
	memset(dp,0,sizeof(dp));
	dp[0][0][0]=1;
	for(int i=1;i<=m;++i)
		for(int j=0;j<=sz;++j)
			for(int k=0;k<26;++k){
				if(ch[ch[j].s[k]].val)
					dp[i][ch[j].s[k]][1]=(dp[i][ch[j].s[k]][1]+dp[i-1][j][0]+dp[i-1][j][1])%MOD;
				else{
					dp[i][ch[j].s[k]][0]=(dp[i][ch[j].s[k]][0]+dp[i-1][j][0])%MOD;
					dp[i][ch[j].s[k]][1]=(dp[i][ch[j].s[k]][1]+dp[i-1][j][1])%MOD;
				}
			}
}

signed main(){
	n=in,m=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		update();
	}
	get_fail();
	query();
	for(int i=0;i<=sz;++i)
		ans=(ans+dp[m][i][1])%MOD;
	printf("%lld\n",ans);
	return 0;
}

看題看清楚,寫數寫仔細
模數寫錯了,D我半小時 😭 😭

T6 數數

fail 樹上下放 val數位dp
數位dp注意維護前導 0 和上限

#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=3e3+5;
const int MOD=1e9+7;
int m,top,sz;
char n[NNN],s[NNN];
struct Trie{
	int s[10];
	int val,fail;
}ch[NNN];
int f[NNN][NNN];

inline void update(){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		if(!ch[p].s[s[i]-'0']) ch[p].s[s[i]-'0']=++sz;
		p=ch[p].s[s[i]-'0'];
	}
	ch[p].val=1;
	return;
}

inline void get_fail(){
	int p=0;queue<int>q;
	for(int i=0;i<10;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<10;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
		}
		ch[u].val+=ch[ch[u].fail].val;
	}
}

inline int dp(int pos/*數位*/,int p/*節點*/,int lim/*上限*/,int lead/*前導零*/){
	if(pos>top) return !lead;//考慮前導零
	if(lim&&f[pos][p]!=-1) return f[pos][p];
	int res=0;
	for(int i=0;i<10;++i){
		if(!lim&&i>n[pos]-'0') break;
		if(lead){
			if(!ch[ch[0].s[i]].val)
				res=(res+dp(pos+1,ch[0].s[i],lim||i<n[pos]-'0',lead&&!i))%MOD;
		}
		else{
			if(!ch[ch[p].s[i]].val)
				res=(res+dp(pos+1,ch[p].s[i],lim||i<n[pos]-'0',0))%MOD;
		}
	}
	if(lim&&!lead) f[pos][p]=res;
	return res;
}

signed main(){
	scanf("%s",n+1);
	top=strlen(n+1);
	m=in;
	for(int i=1;i<=m;++i){
		scanf("%s",s+1);
		update();
	}
	get_fail();
	memset(f,-1,sizeof(f));
	printf("%lld\n",dp(1,0,0,1));
	return 0;
}

T7 阿狸的打字機

定義第xx個打印的字符串爲模式串,第yy個打印的字符串爲文本串(字符串問題)
可以觀察到,模式串末節點被 fail 指了就會有一個貢獻
而只有文本串上的、指向模式串末節點的 fail 纔有貢獻
考慮點亮文本串上的所有節點,跑 fail

衆所周知, fail 是棵樹
考慮樹剖,這玩意 個鬼
DFS序 + 樹狀數組比它優

  • 首先,打字機是個棧,得到所有字符串,建 trie(顯然樹狀數組着建要優一些,棧不優)
  • 然後,邊get_fail 邊建 fail樹DFS序 編號
  • 接着,對於所有詢問,因爲離線,可以排序,對於文本串相同的多組數據,可以優地統計模式串
    (已知區間/區間固定,這也就決定了根據單點改,區間和)
  • 最後,單點修改,遇到根節點,區間求和(差分)統計子樹,注意不往下找的判據是兒子深度比爸爸低(get_fail 的時候沒這個兒子,就跳到 fail 兒子去了),over
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e6+10;
char txt[NNN];
struct Trie{
	int s[26];
	int val,fail;
	int dep;//fail樹上深度 
}ch[NNN];
int f[NNN];//trie上節點父親 
int en[NNN],n;//每個字符串的末節點 
int sz;

inline void build(){
	scanf("%s",txt+1);
	int p=0,len=strlen(txt+1),c;
	for(int i=1;i<=len;++i){
		if(txt[i]=='B'){
			p=f[p];
			continue;
		}
		if(txt[i]=='P'){
			ch[p].val=++n;
			en[n]=p;
			continue;
		}
		c=txt[i]-'a';
		if(!ch[p].s[c]) ch[p].s[c]=++sz;
		f[ch[p].s[c]]=p;
		p=ch[p].s[c];
	}
	return;
}

struct Road{
	int nxt,to;
}road[NNN<<1];
int tot_road,first[NNN];

inline void add(int u,int v){
	++tot_road;
	road[tot_road].nxt=first[u];
	first[u]=tot_road;
	road[tot_road].to=v;
}

inline void get_fail(){
	queue<int>q;
	int p=0;
	for(int i=0;i<26;++i){
		p=ch[0].s[i];
		if(!p) continue;
		q.push(p);
		ch[p].fail=0;
		ch[p].dep=ch[0].dep+1;
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u].s[i];
			if(!p){
				ch[u].s[i]=ch[ch[u].fail].s[i];
				continue;
			}
			q.push(p);
			int v=ch[u].fail;
			while(v&&!ch[v].s[i]) v=ch[v].fail;
			ch[p].fail=ch[v].s[i];
			ch[p].dep=ch[u].dep+1;
		}
		add(u,ch[u].fail),add(ch[u].fail,u);
	}
}

int dfn[NNN];//DFS序/時間戳 
int siz[NNN];//子樹大小 
int tot;//DFS序 

inline void Dfs(int fa,int u){
	int v;
	dfn[u]=++tot;
	siz[u]=1;
	for(int e=first[u];e;e=road[e].nxt){
		v=road[e].to;
		if(!(v^fa)) continue;
		Dfs(u,v);
		siz[u]+=siz[v];
	}
}

int m;
struct Query{
	int x,y,id,ans;
}qur[NNN];
int where[NNN];//記錄排序後每種文本串的第一個詢問 

inline bool cmp1(const Query &u,const Query &v){return u.y<v.y;}
inline bool cmp2(const Query &u,const Query &v){return u.id<v.id;}

inline void get_query(){
	m=in;
	for(int i=1;i<=m;++i){
		qur[i].x=in,qur[i].y=in;
		qur[i].id=i;
	}
	sort(qur+1,qur+m+1,cmp1);
	for(int i=1;i<=m;++i)
		if(qur[i].y^qur[i-1].y)
			where[qur[i].y]=i;
}

int tree[NNN];
inline int lowbit(int x){return x&(-x);}

inline void update(int p,int w){
	while(p<=tot){
		tree[p]+=w;
		p+=lowbit(p);
	}
}

inline int query(int p){
	int res=0;
	while(p){
		res+=tree[p];
		p-=lowbit(p);
	}
	return res;
}

inline void work(int id){
	if(!where[id]) return;
	int i=where[id],p;
	while(true){
		p=en[qur[i].x];
		qur[i].ans=query(dfn[p]+siz[p]-1)-query(dfn[p]-1);
		if(qur[i+1].y^qur[i].y) break;
		++i;
	}
}

inline void search(int u){
	update(dfn[u],1);
	if(ch[u].val) work(ch[u].val);
	int p;
	for(int i=0;i<26;++i){
		p=ch[u].s[i];
		if(!p) continue;
		if(ch[p].dep<=ch[u].dep) continue;
		search(p);
	}
	update(dfn[u],-1);
}

inline void print(){
	sort(qur+1,qur+m+1,cmp2);
	for(int i=1;i<=m;++i)
		printf("%d\n",qur[i].ans);
}

int main(){
	build();
	get_fail();
	Dfs(0,0);
	get_query();
	search(0);
	print();
	return 0;
}

T8 strings

在這裏插入圖片描述
在這裏插入圖片描述
考試題,當時不會,現在會了
ACMDP,稍微改了一下 val 的定義
dpi,jdp_{i,j}表示長度爲ii,字典樹上jj節點的最優方案
考慮轉移方程:
dpi,sonp=max{dpi1,p+valsonp} dp_{i,son_p}=\max\{dp_{i-1,p}+val_{son_p}\}

#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e3+10;
const int INF=1e9+10;
int n,L,a[NNN],sz;
char s[NNN];
int ch[NNN][26],fail[NNN],val[NNN];
int dp[NNN][NNN],ans;
int same[NNN],idx[NNN];

inline void build(int id){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		if(!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++sz;
		p=ch[p][s[i]-'a'];
	}
	if(idx[p]) same[id]=idx[p],val[p]+=a[id];
	else val[p]=a[id],idx[p]=id;
}

inline void get_fail(){
	queue<int>q;int p=0;
	for(int i=0;i<26;++i){
		p=ch[0][i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u][i];
			if(!p){
				ch[u][i]=ch[fail[u]][i];
				continue;
			}
			q.push(p);
			int v=fail[u];
			while(v&&!ch[v][i]) v=fail[v];
			fail[p]=ch[v][i];
		}
		val[u]+=val[fail[u]];
	}
}

inline void query(){
	for(int i=0;i<=L;++i)
		for(int j=0;j<=sz;++j)
			dp[i][j]=-INF;
	dp[0][0]=0;
	for(int i=1;i<=L;++i)
		for(int j=0;j<=sz;++j)
			for(int c=0;c<26;++c)
				dp[i][ch[j][c]]=max(dp[i][ch[j][c]],dp[i-1][j]+val[ch[j][c]]);
}

signed main(){
	n=in,L=in;
	for(int i=1;i<=n;++i) a[i]=in;
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		build(i);
	}
	get_fail();
	query();
	for(int i=0;i<=sz;++i){
		if(!same[i]) ans=max(ans,dp[L][i]);
		else ans=max(ans,dp[L][same[i]]);
	}
	printf("%lld\n",ans);
	return 0;
}

T8 e-Government

類似 T7,且顯然暴力 T
但是不像 T7,在線,沒有排序的優化了,O(nlogn)O(n\log n)
無法排序也就決定區間不定,因此只能單點改,區間和

首先,建 trie,邊 get_fail 邊建樹
然後,DFS排序,區間加上傳現有單詞集
最後,對於每個操作,區間加/單點改

#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
	int i=0,f=1;char ch;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')ch=getchar(),f=-1;
	while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e6+10;
int n,m,sz;
char s[NNN];
int ch[NNN][26],fail[NNN];
int where[NNN];
int exist[NNN];

inline void build(int id){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		if(!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++sz;
		p=ch[p][s[i]-'a'];
	}
	where[id]=p;
}

int tot,fst[NNN],nxt[NNN],aim[NNN];

inline void add(int u,int v){
	++tot;
	nxt[tot]=fst[u];
	fst[u]=tot;
	aim[tot]=v;
}

inline void get_fail(){
	int p=0;queue<int>q;
	for(int i=0;i<26;++i){
		p=ch[0][i];
		if(!p) continue;
		q.push(p);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;++i){
			p=ch[u][i];
			if(!p){
				ch[u][i]=ch[fail[u]][i];
				continue;
			}
			q.push(p);
			int v=ch[u][i];
			while(v&&!ch[v][i]) v=fail[v];
			fail[p]=ch[v][i];
		}
		add(u,fail[u]),add(fail[u],u);
	}
}

int tol,dfn[NNN],siz[NNN];

inline void DFS(int fa,int u){
	dfn[u]=++tol,siz[u]=1;
	for(int v,e=fst[u];e;e=nxt[e]){
		v=aim[e];
		if(v==fa)continue;
		DFS(u,v);
		siz[u]+=siz[v];
	}
}

int tr[NNN];
inline int lowbit(int x){return x&(-x);}

inline void update(int p,int w){
	while(p<=tol){
		tr[p]+=w;
		p+=lowbit(p);
	}
}

inline int query(int p){
	int res=0;
	while(p){
		res+=tr[p];
		p-=lowbit(p);
	}
	return res;
}

inline int sum(){
	int ans=0,p=0,len=strlen(s+1);
	for(int i=1;i<=len;++i){
		p=ch[p][s[i]-'a'];
		ans+=query(dfn[p]);
	}
	return ans;
}

int main(){
	n=in,m=in;
	for(int i=1;i<=m;++i){
		scanf("%s",s+1);
		build(i);
	}
	get_fail();
	DFS(0,0);
	for(int i=1;i<=m;++i){
		exist[i]=true;
		int u=where[i];
		update(dfn[u],1);
		update(dfn[u]+siz[u],-1);
	}
	for(int i=1;i<=n;++i){
		char opt=getchar();
		while(opt!='+'&&opt!='?'&&opt!='-') opt=getchar();
		if(opt=='?'){
			scanf("%s",s+1);
			printf("%d\n",sum());
		}else if(opt=='+'){
			int id=in;
			if(exist[id]) continue;
			exist[id]=true;
			int u=where[id];
			update(dfn[u],1);
			update(dfn[u]+siz[u],-1);
		}else{
			int id=in;
			if(!exist[id]) continue;
			exist[id]=false;
			int u=where[id];
			update(dfn[u],-1);
			update(dfn[u]+siz[u],1);
		}
	}
}

假裝我過了

T9 Indie Album

這黑題好像跟 T7 沒什麼差別

咕掉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章