2017 ACM/ICPC 南宁 D Banned Patterns

题目链接:

https://nanti.jisuanke.com/t/19970

 

思路:

KMP做这道题的想法
模板ABCA变成 -1 -1 -1 3
询问CBAABCA变成 -1 -1 -1 1 3 5 3(记作diff)
 
next数组是 0  1  2 3
匹配CBAABCA的时候,我记f[i]为匹配到第i个字符时候,模板串最长能匹配到哪,模板串匹配的最长位置为j
这样f就是  1 2 3 1 2 3 4
 
为什么f[4]变成了1,因为匹配到第四个的时候,要求s[4]==s[4-1],j一直跳到1,这时候diff[4]>=next,意味着这个diff[4]已经没用了,要变成-1
 
这样diff就变成了 -1 -1 -1 -1 3 5 3,此时j指向1
 
然后求f[5],发现5-3>=j,就把3变成-1,来匹配,因为让s[5]==s[2]已经没有用了,模板串长度不够,不需要考虑这个3
 
这样diff又变成了-1 -1 -1 -1 -1 5 3,此时next匹配上了,变成2
 
一直下去,求出所有的f

 

复杂度不会算,因为询问串每次都跳fail,不知道会覆盖多少个不同的状态,最坏情况是可能会是len^2?

 

#include<bits/stdc++.h>
using namespace std;

const int N=100005;

int fail[N],cnt[N],deep[N];
map<int,int>ch[N];
int tot,root;
int newnode(){
	tot++;
	ch[tot].clear();
	fail[tot]=0;  cnt[tot]=0;
	return tot;
}
void init(){
	for (int i=0;i<=tot;i++) ch[i].clear();
	root=newnode();
}
void insert(int *s,int n){
	int now=root;
	int _deep=0;
	for (int i=1;i<=n;i++){
		_deep++;
		int w=s[i];
		assert(w<_deep);
		if (!ch[now].count(w)) ch[now][w]=newnode();
		now=ch[now][w];
		deep[now]=_deep;
	}
	cnt[now]++;
}
int getnext(int u,int w){
	if (w>=deep[u]+1) w=-1;
	// for (auto &p:ch[u]){
	// 	printf("ch %d:%d %d\n",u,p.first,p.second);
	// }
	// printf("%d %d %d \n",u,w,ch[u].count(w));
	while(u!=root&&!ch[u].count(w)){
		u=fail[u];
		if (w>=deep[u]+1) w=-1;
	}
	if (ch[u].count(w)) return ch[u][w];
	return u;
}
void build(){
	queue<int>Q;
	fail[root]=root;
	for (auto &p:ch[root]){
		int v=p.second;
		fail[v]=root;
		Q.push(v);
	}
	while(!Q.empty()){
		int u=Q.front(); Q.pop();
		for (auto &p:ch[u]){
			int v=p.second;
			fail[v]=getnext(fail[u],p.first);
			Q.push(v);
		}
	}
}
int T;
int vis[N];
bool query(int *s,int n){
	int now=root;
	for (int i=1;i<=n;i++){
		int w=s[i];
		now=getnext(now,w);
		for (int v=now;v!=root&&vis[v]!=T;v=fail[v]){
			vis[v]=T;
			if (cnt[v]) return 1;
		}
	}
	return 0;
}

char s[N*10];
int a[N*10];
int pre[26];
int main(){
	int t; scanf("%d",&t);
	for (int cas=1;cas<=t;cas++){
		init();
		int n; scanf("%d",&n);
		for (int i=1;i<=n;i++){
			memset(pre,0,sizeof(pre));
			scanf("%s",s+1);
			int m=0;
			for (int j=1;s[j];j++){
				int w=s[j]-'A';
				if (pre[w]) a[j]=j-pre[w];
				else a[j]=-1;
				pre[w]=j;
				m++;
				// printf("%d ",a[j]);
			}
			insert(a,m);
		}
		build();
		// for (int i=1;i<=tot;i++) printf("%d %d %d\n",i,fail[i],deep[i]);
		scanf("%d",&n);
		printf("Case #%d: ",cas);
		for (int i=1;i<=n;i++){
			T++;
			memset(pre,0,sizeof(pre));
			scanf("%s",s+1);
			int m=0;
			for (int j=1;s[j];j++){
				int w=s[j]-'A';
				if (pre[w]) a[j]=j-pre[w];
				else a[j]=-1;
				pre[w]=j;
				m++;
			}
			if (query(a,m)) printf("Y");
			else printf("N");
			if (i==n) printf("\n");
			else printf(" ");
		}

	}
}

 

Educational Codeforces Round 16 F - String Set Queries (CDQ分治+AC自动机)

题目链接:

http://codeforces.com/contest/710/problem/F

 

题意:

1. 集合加入一个串

2. 集合删除一个串

3. 问集合中的串在给定串中出现的次数和(可重复)

要求强制在线

 

思路:

如果是离线的话,直接cdq分治,每次求左半边的AC自动机去更新右半边

强制在线的话,只要保存一下log个左半边的AC自动机就好了

 

复杂度:O((n+s)log(n))

#include<bits/stdc++.h>
using namespace std;

const int N=900005;

int ch[N][26],fail[N],cnt[N],f[N];
int tot;
int newnode(){
	tot++;
	for (int i=0;i<26;i++) ch[tot][i]=0;
	fail[tot]=0;  cnt[tot]=0;
	return tot;
}
int merge(int x,int y){
	if (!x||!y) return x+y;
	cnt[x]+=cnt[y];
	for (int i=0;i<26;i++)
		ch[x][i]=merge(ch[x][i],ch[y][i]);
	return x;
}

int root[20];
void insert(int root,char *s,int y){
	int now=root;
	for (int i=0;s[i];i++){
		int w=s[i]-'a';
		if (!ch[now][w]) ch[now][w]=newnode();
		now=ch[now][w];
	}
	cnt[now]+=y;
}
int go(int root,int u,int w){
	while(u!=root&&!ch[u][w]) u=fail[u];
	if (ch[u][w]) return ch[u][w];
	return u;
}
void build(int root){
	queue<int>Q;
	fail[root]=root;
	for (int i=0;i<26;i++){
		int v=ch[root][i];
		if (v){
			fail[v]=root;
			Q.push(v);
		}
	}
	while(!Q.empty()){
		int u=Q.front(); Q.pop();
		f[u]=f[fail[u]]+cnt[u];
		for (int i=0;i<26;i++){
			int v=ch[u][i];
			if (v){
				fail[v]=go(root,fail[u],i);
				Q.push(v);
			}
		}
	}
}
int query(int root,char *s){
	int ret=0;
	int now=root;
	for (int i=0;s[i];i++){
		int w=s[i]-'a';
		now=go(root,now,w);
		ret+=f[now];
	}
	return ret;
}
void init(int &root){
	root=newnode();
	build(root);
}

int tp;
char s[N];
int solve(int l,int r,int deep){
	if (l==r){
		cin>>tp>>s;
		init(root[deep]);
		if (tp<3){
			int y;
			if (tp==1) y=1; else y=-1;
			insert(root[deep],s,y);
		}
		else{
			long long ans=0;
			for (int d=1;d<deep;d++) ans+=query(root[d],s);
			cout<<ans<<endl;
			fflush(stdout);
		}
		return root[deep];
	}
	init(root[deep]);
	int mid=(l+r)/2;
	int rtl=solve(l,mid,deep+1);
	root[deep]=merge(root[deep],rtl);
	build(root[deep]);
	int rtr=solve(mid+1,r,deep+1);
	root[deep]=merge(root[deep],rtr);
	build(root[deep]);
	return root[deep];
}
int main(){
	ios::sync_with_stdio(false);
	int n; cin>>n;
	solve(1,n,1);
}