AC自动机

学习博客

POJ 1625

题意:给出包含n个字符的字符集,以下所提字符串均由该字符集中的字符构成。给出p个长度不超过10的字符串,求长为m且不包含上述p个字符串的字符串有多少个。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct NUM{
	int t,a[103];
}ans,f[53][103];
struct node{
	int las,fail,p[52],v;
}T[103];
int n,m,k,i,j,c,sum,q[1003],t;
char s[53],s1[53];
int get(char ch){
	for (int i=0;i<n;i++)
		if (s1[i]==ch) return i;
}
void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=get(s[i]);
		if (!T[k].p[ch]) T[k].p[ch]=++sum;
		k=T[k].p[ch];
	}
	T[k].v=1;
}
void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<n;i++){
			int v=T[u].p[i];
			if (v){
				int k=T[u].fail;
				while (k && !T[k].p[i]) k=T[k].fail;
				k=T[k].p[i];
				if (u) T[v].fail=k,T[v].las=T[k].v?k:T[k].las;
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int query(int x,int y){
	int tot=0;
	for (int i=0;i<n;i++)
		if (T[x].p[i]==y) tot++;
	return tot;
}
NUM operator +(NUM x,NUM y){
	NUM z;
	z.t=max(x.t,y.t);
	memset(z.a,0,sizeof(z.a));
	for (int i=0;i<z.t;i++){
		if (i<x.t) z.a[i]+=x.a[i];
		if (i<y.t) z.a[i]+=y.a[i];
		z.a[i+1]+=z.a[i]/10,z.a[i]%=10;
	}
	if (z.a[z.t]) z.t++;
	return z;
}
void print(NUM x){
	if (x.t) for (int i=x.t-1;i>=0;i--) printf("%d",x.a[i]);
	else printf("0");
	putchar('\n');
}
int main(){
	scanf("%d%d%d%s",&n,&m,&k,s1);
	for (i=1;i<=k;i++) scanf("%s",s),insert(s);
	build_ac();
	f[0][0].t=f[0][0].a[0]=1;
	for (i=1;i<=m;i++)
		for (j=0;j<=sum;j++)
			if (!T[j].v && !T[T[j].las].v)
				for (k=0;k<=sum;k++)
					if (!T[k].v && !T[T[k].las].v)
						for (c=0,t=query(k,j);c<t;c++) f[i][j]=f[i][j]+f[i-1][k];
	for (i=0;i<=sum;i++) ans=ans+f[m][i];
	print(ans);
}

POJ 2778. DNA Sequence

题意:给m个病毒串,问不包含病毒串的长度n的DNA片段有几个。

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int M=100000;
struct ma{
	ll a[102][102];
}A,B;
struct kk{
	int fail,v,p[4];
}T[103];
int ans,i,j,sum,v,n,m,mp[130],q[103];
char s[12];
ma operator *(ma x,ma y){
	ma z;
	memset(z.a,0,sizeof(z.a));
	for (int i=0;i<=sum;i++)
		for (int j=0;j<=sum;j++)
			for (int k=0;k<=sum;k++) z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%M;
	return z;
}
void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=mp[s[i]];
		if (!T[k].p[ch]) T[k].p[ch]=++sum;
		k=T[k].p[ch];
	}
	T[k].v=1;
}
void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<4;i++){
			int v=T[u].p[i];
			if (v){
				int k=T[u].fail;
				while (k && !T[k].p[i]) k=T[k].fail;
				k=T[k].p[i];
				if (u) T[v].fail=k,T[v].v|=T[k].v;
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int main(){
	mp['C']=1;mp['G']=2;mp['T']=3;
	scanf("%d%d",&m,&n);
	for (i=1;i<=m;i++) scanf("%s",s),insert(s);
	build_ac();
	for (i=0;i<=sum;i++)
		if (!T[i].v)
			for (j=0;j<4;j++){
				v=T[i].p[j];
				if (!T[v].v) A.a[i][v]++;
			}
	for (i=0;i<=sum;i++) B.a[i][i]=1;
	for (;n;n>>=1,A=A*A)
		if (n&1) B=B*A;
	for (i=0;i<=sum;i++) ans=(ans+B.a[0][i])%M;
	printf("%d",ans);
}

HDU 2457. DNA repair

题意:给出一些不合法的模式DNA串,给出一个原串,问最少需要修改多少个字符,使得原串中不包含非法串

#include<cstdio>
#include<cstring>
struct kk{
	int fail,v,p[4];
}T[1003];
int mp[130],n,l,f[2][1003],q[1003],sum,i,j,k,mn,cas,x,xx;
char s[1003];
inline int min(int x,int y){
	return x<y?x:y;
}
inline void init(int x){
	T[x].fail=T[x].v=0;
	memset(T[x].p,0,sizeof(T[x].p));
}
inline void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=mp[s[i]];
		if (!T[k].p[ch]) init(++sum),T[k].p[ch]=sum;
		k=T[k].p[ch];
	}
	T[k].v=1;
}
inline void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<4;i++){
			int v=T[u].p[i];
			if (v){
				int k=T[u].fail;
				while (k && !T[k].p[i]) k=T[k].fail;
				k=T[k].p[i];
				if (u) T[v].fail=k,T[v].v|=T[k].v;
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int main(){
	mp['C']=1;mp['G']=2;mp['T']=3;
	while (~scanf("%d",&n)){
		if (!n) return 0;
		init(0);sum=0;
		for (i=0;i<n;i++) scanf("%s",s),insert(s);
		build_ac();
		scanf("%s",s);
		l=strlen(s);
		memset(f[0],63,sizeof(f[0]));
		f[0][0]=0;
		x=0;xx=1;
		for (i=0;i<l;i++,x^=1,xx^=1){
			memset(f[xx],63,sizeof(f[xx]));
			for (j=0;j<=sum;j++)
				if (f[x][j]<1e8)
					for (k=0;k<4;k++)
						if (!T[T[j].p[k]].v) f[xx][T[j].p[k]]=min(f[xx][T[j].p[k]],f[x][j]+(k!=mp[s[i]]));
		}
		mn=1e9;
		for (i=0;i<=sum;i++) mn=min(mn,f[x][i]);
		printf("Case %d: %d\n",++cas,mn<1e8?mn:-1);
	}
}

HDU 2825. Wireless Password

题意:问长度n至少包含k个咒语的字符串有多少个

#include<cstdio>
#include<cstring>
using namespace std;
const int M=20090717;
struct kk{
	int fail,v,p[26];
}T[103];
int q[103],n,m,p,i,k,x,t,ans,f[27][103][1<<10],sum,j;
char s[13];
void mod(int &x){
	if (x>=M) x-=M;
}
int get(int x){
	int s=0;
	for (;x;x-=x&-x) s++;
	return s;
}
void build(int x){
	T[x].fail=T[x].v=0;
	memset(T[x].p,0,sizeof(T[x].p));
}
void insert(char *s,int x){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=s[i]-'a';
		if (!T[k].p[ch]) build(++sum),T[k].p[ch]=sum;
		k=T[k].p[ch];
	}
	T[k].v=1<<x;
}
void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<26;i++){
			int v=T[u].p[i];
			if (v){
				int k=T[u].fail;
				while (k && !T[k].p[i]) k=T[k].fail;
				k=T[k].p[i];
				if (u) T[v].fail=k,T[v].v|=T[k].v;
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int main(){
	while (1){
		scanf("%d%d%d",&n,&m,&p);
		if (!n && !m && !p) return 0;
		sum=0;
		build(0);
		for (i=0;i<m;i++) scanf("%s",s),insert(s,i);
		build_ac();
		memset(f,0,sizeof(f));
		f[0][0][0]=1;
		for (i=0;i<n;i++)
			for (j=0;j<=sum;j++)
				for (k=0;k<(1<<m);k++)
					if (f[i][j][k])
						for (t=0;t<26;t++){
							x=T[j].p[t];
							mod(f[i+1][x][k|T[x].v]+=f[i][j][k]);
						}
		ans=0;
		for (i=0;i<=sum;i++)
			for (j=0;j<(1<<m);j++)
				if (get(j)>=p) mod(ans+=f[n][i][j]);
		printf("%d\n",ans);
	}
}

HDU 4057. Rescue the Rabbit

题意:有ATCG4个字母,要求构成一个长度为l的字符串,给出n个模式串及其权值,问能构成的字符串中最大的权值和是多少,每个模式串只计算一次

#include<cstdio>
#include<cstring>
using namespace std;
struct kk{
	int fail,v,p[4],num;
}T[1003];
int q[1003],n,m,p,i,k,x,t,ans,f[2][1003][1<<11],sum,j,mp[130],C,tmp,ne,x1,x2;
char s[103];
void mx(int &x,int y){
	if (y>x) x=y;
}
void build(int x){
	T[x].fail=T[x].v=T[x].num=0;
	memset(T[x].p,0,sizeof(T[x].p));
}
void insert(char *s,int x,int y){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=mp[s[i]];
		if (!T[k].p[ch]) build(++sum),T[k].p[ch]=sum;
		k=T[k].p[ch];
	}
	T[k].v|=1<<x;
	T[k].num+=y;
}
void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<4;i++){
			int v=T[u].p[i];
			if (v){
				if (u) T[v].fail=T[T[u].fail].p[i];
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int main(){
	mp['C']=1;mp['G']=2;mp['T']=3;
	while (~scanf("%d%d",&m,&n)){
		sum=0;build(0);
		for (i=0;i<m;i++) scanf("%s%d",s,&x),insert(s,i,x);
		build_ac();
		memset(f[0],-63,sizeof(f[0]));
		f[0][0][0]=0;
		x1=0;x2=1;
		for (i=0;i<n;i++,x1^=1,x2^=1){
			memset(f[x2],-63,sizeof(f[x2]));
			for (j=0;j<=sum;j++)
				for (k=0;k<(1<<m);k++)
					if (f[x1][j][k]>-1e9)
						for (t=0;t<4;t++){
							ne=x=T[j].p[t];C=tmp=0;
							while (x){
								C|=T[x].v;
								if (!(k&T[x].v)) tmp+=T[x].num;
								x=T[x].fail;
							}
							mx(f[x2][ne][k|C],f[x1][j][k]+tmp);
						}
		}
		ans=-1e9;
		for (i=0;i<=sum;i++)
			for (j=0;j<(1<<m);j++) mx(ans,f[x1][i][j]);
		if (ans>=0) printf("%d\n",ans);
		else printf("No Rabbit after 2012!\n");
	}
}

HDU 3962. Microgene

题意:给定m个DNA病毒序列,求碱基构成的长度为n且含有两个以上DNA病毒序列,结果对10007取模。

#include<cstdio>
#include<cstring>
using namespace std;
const int P=10007;
int mp[130],sum,n,m,i,k,j,ans,x,size,q[33];
char s[7];
struct node{
	int fail,p[4],v;
}T[33];
struct M{
	int a[63][63];
}A;
M operator *(M A,M B){
	M C;
	memset(C.a,0,sizeof(C.a));
	for (int i=0;i<size;i++)
		for (int j=0;j<size;j++)
			for (int k=0;k<size;k++) C.a[i][j]=(C.a[i][j]+A.a[i][k]*B.a[k][j])%P;
	return C;
}
M operator ^(M A,int n){
	M E;
	memset(E.a,0,sizeof(E.a));
	for (int i=0;i<size;i++) E.a[i][i]=1;
	for (;n;n>>=1,A=A*A)
		if (n&1) E=E*A;
	return E;
}
void init(int x){
	T[x].fail=T[x].v=0;
	memset(T[x].p,0,sizeof(T[x].p));
}
void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=mp[s[i]];
		if (!T[k].p[ch]) init(++sum),T[k].p[ch]=sum;
		k=T[k].p[ch];
	}
	T[k].v=1;
}
void build_ac(){
	int h=0,t=1;
	q[0]=0;
	while (h<t){
		int u=q[h++];
		for (int i=0;i<4;i++){
			int v=T[u].p[i];
			if (v){
				k=T[T[u].fail].p[i];
				if (u) T[v].fail=k,T[v].v|=T[k].v;
				q[t++]=v;
			}else T[u].p[i]=T[T[u].fail].p[i];
		}
	}
}
int main(){
	mp['C']=1;mp['G']=2;mp['T']=3;
	while (~scanf("%d%d",&n,&m)){
		init(sum=0);
		for (i=0;i<n;i++) scanf("%s",s),insert(s);
		build_ac();
		sum++;
		memset(A.a,0,sizeof(A.a));
		for (i=0;i<sum;i++)
			for (j=0;j<4;j++){
				k=T[i].p[j];
				if (T[k].v) A.a[i][k+sum]++;
				else A.a[i][k]++,A.a[i+sum][k+sum]++;
			}
		size=sum<<1;A=A^m;ans=1;
		for (x=4;m;m>>=1,x=x*x%P)
			if (m&1) ans=ans*x%P;
		for (i=0;i<size;i++) ans=(ans-A.a[0][i])%P;
		printf("%d\n",(ans+P)%P);
	}
}

HDU 2243. 考研路茫茫 —— 单词情结

题意:问长度为1~N的串中包含了模式串的串总共有几个。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef unsigned __int64 ll;
int sum,n,m,i,k,j,l,l1;
ll all,left;
char s[9];
struct node{
    node *fail,*p[26],*jump[26];
    int v;
}rt[53];
queue<node*>q;
struct M{
    ll a[53][53];
    void clear(){memset(a,0,sizeof(a));}
}A,B,e;
M operator *(M A,M B){
    M C;
    C.clear();
    for (int i=0;i<sum;i++)
        for (int j=0;j<sum;j++)
            for (int k=0;k<sum;k++) C.a[i][j]+=A.a[i][k]*B.a[k][j];
    return C;
}
M operator ^(M A,int n){
    M E=e;
    for (;n;n/=2,A=A*A)
        if (n&1) E=E*A;
    return E;
}
void insert(char *s){
    node *k=rt;
    for (int i=0;s[i];i++){
        int ch=s[i]-'a';
        if (!k->p[ch]){
            k->p[ch]=rt+sum;
            memset(rt+sum,0,sizeof(struct node));
            sum++;
        }
        k=k->p[ch];
    }
    k->v=1;
}
void build_ac(){
    q.push(rt);
    rt->fail=NULL;
    while (!q.empty()){
        node *u=q.front();q.pop();
        for (int i=0;i<26;i++){
            node *k=u->fail,*v=u->p[i];
            while (k && !k->p[i]) k=k->fail;
            if (v){
                if (!k) v->fail=rt;
                else v->fail=k->p[i],v->v|=k->p[i]->v;
                q.push(v);
                u->jump[i]=v;
            }else{
                if (!k) u->jump[i]=rt;
                else u->jump[i]=k->p[i];
            }
        }
    }
}
int main(){
    for (i=0;i<52;i++) e.a[i][i]=1;
    while (scanf("%d%d",&n,&m)==2){
        sum=1;memset(rt,0,sizeof(struct node));
        for (i=0;i<n;i++) scanf("%s",s),insert(s);
        build_ac();
        A.clear();l=0;
        for (i=0;i<sum;i++)
            if (!rt[i].v){
                l1=0;
                for (j=0;j<sum;j++)
                    if (!rt[j].v){
                        for (k=0;k<26;k++)
                            if (rt[j].jump[k]==rt+i) A.a[l][l1]++;
                        l1++;
                    }
                l++;
            }
        sum=l*2;
        for (i=0;i<l;i++) A.a[i][i+l]=A.a[i+l][i+l]=1;
        m++;A=A^m;
        left=0;
        for (i=0;i<l;i++) left+=A.a[i][l];
        B.a[0][0]=26;B.a[0][1]=B.a[1][1]=1;B.a[1][0]=0;
        sum=2;B=B^m;
        all=B.a[0][1]-1;
        left--;all-=left;
        printf("%I64u\n",all);
    }
}

Hdu 4117 GRE Words

题意:给定n个字符串,要求按顺序取一些字符串,满足后一个字符串是前一个字符串的子串,要求使得取出的权值和最大

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define mid (l+r>>1)
#define M(a) memset(a,0,sizeof(a))
const int N=300003,M=20003;
struct kk{
	int to,ne;
}e[N];
int p[N][26],fail[N],T,n,sum,tim,i,j,ans,tmp,le[N],ri[N],h[N],tot,tr[N<<2],w[M],now,cas,tag[N<<2];
queue<int>q;
char s1[N];
string s[M];
inline void init(int x){
	M(p[x]);fail[x]=0;
}
inline void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=s[i]-'a';
		if (!p[k][ch]) init(++sum),p[k][ch]=sum;
		k=p[k][ch];
	}
}
inline void build_ac(){
	q.push(0);
	while (!q.empty()){
		int u=q.front();q.pop();
		for (int i=0;i<26;i++){
			int v=p[u][i];
			if (v){
				if (u) fail[v]=p[fail[u]][i];
				q.push(v);
			}else p[u][i]=p[fail[u]][i];
		}
	}
}
inline void addedge(int x,int y){
	e[++tot]=(kk){y,h[x]};
	h[x]=tot;
}
inline void dfs(int u){
	le[u]=++tim;
	for (int i=h[u];i;i=e[i].ne) dfs(e[i].to);
	ri[u]=tim;
}
inline void down(int t){
	if (tag[t]){
		tr[t<<1]=max(tr[t<<1],tag[t]);
		tr[t<<1|1]=max(tr[t<<1|1],tag[t]);
		tag[t<<1]=max(tag[t<<1],tag[t]);
		tag[t<<1|1]=max(tag[t<<1|1],tag[t]);
		tag[t]=0;
	}
}
inline int query(int t,int l,int r,int x){
	if (l==r) return tr[t];
	down(t);
	if (x<=mid) return query(t<<1,l,mid,x);
	return query(t<<1|1,mid+1,r,x);
}
inline void update(int t,int l,int r,int x,int y,int val){
	if (x<=l && r<=y){
		tr[t]=max(tr[t],val);
		tag[t]=max(tag[t],val);
		return;
	}
	down(t);
	if (x<=mid) update(t<<1,l,mid,x,y,val);
	if (mid<y) update(t<<1|1,mid+1,r,x,y,val);
	tr[t]=max(tr[t<<1],tr[t<<1|1]);
}
int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%d",&n);
		init(sum=0);
		ans=tim=tot=0;
		M(h);M(tr);M(tag);
		for (i=0;i<n;i++){
			scanf("%s%d",s1,&w[i]);
			s[i].clear();
			for (j=0;s1[j];j++) s[i]+=s1[j];
			insert(s1);
		}
		build_ac();
		for (i=1;i<=sum;i++) addedge(fail[i],i);
		dfs(0);
		for (i=0;i<n;i++){
			tmp=now=0;
			for (j=0;s[i][j];j++){
				now=p[now][s[i][j]-'a'];
				tmp=max(tmp,query(1,1,tim,le[now]));
			}
			tmp+=w[i];
			update(1,1,tim,le[now],ri[now],tmp);
			ans=max(ans,tmp);
		}
		printf("Case #%d: %d\n",++cas,ans);
	}
}

Hdu 3341 Lost’s revenge

题解
题意:给你多个模板串,然后在给你一个文本串.所有串均只由A,C,G,T,4个字母构成,问你文本串的字符重新排序后最多能有多少个模板串.

#include<bits/stdc++.h>
using namespace std;
struct node{
	int mark,fail,ne[5];
}T[503];
int Case,mp[128],k[5],a[5],i,j,l,now,ans,len,n,f[503][15003],sum,v[5];
char s[203];
queue<int>q;
void init(int x){
	T[x].mark=0;
	memset(T[x].ne,0,sizeof(T[x].ne));
}
void insert(char *s){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=mp[s[i]];
		if (!T[k].ne[ch]) init(++sum),T[k].ne[ch]=sum;
		k=T[k].ne[ch];
	}
	T[k].mark++;
}
void build(){
	q.push(0);
	while (!q.empty()){
		int u=q.front();q.pop();
		for (int i=1;i<=4;i++)
			if (T[u].ne[i]){
				T[T[u].ne[i]].fail=u?T[T[u].fail].ne[i]:0;
				q.push(T[u].ne[i]);
			}else T[u].ne[i]=T[T[u].fail].ne[i];
		T[u].mark+=T[T[u].fail].mark;
	}
}
int main(){
	mp['A']=1;mp['C']=2;mp['G']=3;mp['T']=4;
	while (1){
		scanf("%d",&n);
		if (!n) break;
		init(sum=0);
		for (i=1;i<=n;i++) scanf("%s",s),insert(s);
		build();
		scanf("%s",s+1);
		len=strlen(s+1);
		memset(v,0,sizeof(v));
		for (i=1;i<=len;i++) v[mp[s[i]]]++;
		k[4]=1;
		k[3]=v[4]+1;
		k[2]=k[3]*(v[3]+1);
		k[1]=k[2]*(v[2]+1);
		memset(f,-1,sizeof(f));
		f[0][0]=0;ans=0;
		for (l=0;l<=len;l++)
			for (i=0;i<=sum;i++)
				for (a[1]=max(l-v[2]-v[3]-v[4],0);a[1]<=min(l,v[1]);a[1]++)
					for (a[2]=max(l-a[1]-v[3]-v[4],0);a[2]<=min(l,v[2]);a[2]++)
						for (a[3]=max(l-a[1]-a[2]-v[4],0);a[3]<=min(l,v[3]);a[3]++){
							a[4]=l-a[1]-a[2]-a[3];
							now=a[1]*k[1]+a[2]*k[2]+a[3]*k[3]+a[4];
							if (f[i][now]>=0)
								for (j=1;j<=4;j++)
									if (a[j]<v[j]){
										f[T[i].ne[j]][now+k[j]]=max(f[T[i].ne[j]][now+k[j]],f[i][now]+T[T[i].ne[j]].mark);
										ans=max(ans,f[T[i].ne[j]][now+k[j]]);
									}
						}
		printf("Case %d: %d\n",++Case,ans);
	}
}

bzoj 2434 阿狸的打字机

#include<bits/stdc++.h>
using namespace std;
const int N=100003;
struct kk{
    int p[26],fail;
}T[N];
struct node{
    int to,ne;
}e[N];
vector<int>G[N],w[N];
char s[N];
int fa[N],in[N],out[N],dfn,tot,sum,h[N],i,x,y,n,tr[N],pos[N],q[N],ans[N];
int query(int x){
    int s=0;
    for (;x;x-=x&-x) s+=tr[x];
    return s;
}
void add(int x,int y){
    for (;x<=dfn;x+=x&-x) tr[x]+=y;
}
void build(){
    int u=0,id=0;
    for (int i=0;s[i];i++){
        if (s[i]=='B') u=fa[u];
        else if (s[i]=='P') pos[++id]=u;
        else{
            int ch=s[i]-'a';
            if (!T[u].p[ch]) T[u].p[ch]=++sum,fa[sum]=u;
            u=T[u].p[ch];
        }
    }
}
void build_ac(){
    int h=0,t=1;
    q[0]=0;
    while (h<t){
        int u=q[h++];
        for (int i=0;i<26;i++){
            int &v=T[u].p[i],ch=T[T[u].fail].p[i];
            if (v){
                q[t++]=v;
                if (u) T[v].fail=ch;
            }else v=ch;
        }
    }
}
void solve(){
    int u=0,id=0;
    for (int i=0;s[i];i++){
        if (s[i]=='B') add(in[u],-1),u=fa[u];
        else if (s[i]=='P'){
            id++;
            for (int j=0;j<G[id].size();j++){
                int v=pos[G[id][j]];
                ans[w[id][j]]=query(out[v])-query(in[v]-1);
            }
        }else u=T[u].p[s[i]-'a'],add(in[u],1);
    }
}
void addedge(int x,int y){
    e[++tot]=(node){y,h[x]};
    h[x]=tot;
}
void dfs(int u){
    in[u]=++dfn;
    for (int i=h[u];i;i=e[i].ne) dfs(e[i].to);
    out[u]=dfn;
}
int main(){
    scanf("%s%d",s,&n);
    build();
    build_ac();
    for (i=1;i<=sum;i++) addedge(T[i].fail,i);
    for (i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        G[y].push_back(x);
        w[y].push_back(i);
    }
    dfs(0);
    solve();
    for (i=1;i<=n;i++) printf("%d\n",ans[i]);
}

Hdu 3247 Resource Archiver

题解
题意:给n个01串和m个病毒串,要求用01串拼出最短的不包含病毒串的串,输出最短的长度

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=60003,M=203;
struct node{
	int ne[2],fail,end;
	bool vir;
}T[N];
char s[1003];
int pos[M],d[N],G[M][M],sum,cnt,dp[1<<11][M],n,m,i;
queue<int>q;
void init(int x){
	T[x].ne[0]=T[x].ne[1]=T[x].end=T[x].vir=T[x].fail=0;
}
void insert(char *s,int x){
	int k=0;
	for (int i=0;s[i];i++){
		int ch=s[i]^48;
		if (!T[k].ne[ch]) init(++sum),T[k].ne[ch]=sum;
		k=T[k].ne[ch];
	}
	if (x>=0) T[k].end=1<<x;
	else T[k].vir=1;
}
void build(){
	q.push(0);
	while (!q.empty()){
		int u=q.front();q.pop();
		for (int i=0;i<2;i++)
			if (T[u].ne[i]){
				int v=T[u].ne[i];
				if (u) T[v].fail=T[T[u].fail].ne[i];
				T[v].end|=T[T[v].fail].end;
				T[v].vir|=T[T[v].fail].vir;
				q.push(v);
			}else T[u].ne[i]=T[T[u].fail].ne[i];
	}
}
void path(int k){
	memset(d,63,sizeof(d));
	q.push(pos[k]);
	d[pos[k]]=0;
	while (!q.empty()){
		int u=q.front();q.pop();
		for (int i=0;i<2;i++){
			int v=T[u].ne[i];
			if (d[v]>1e9 && !T[v].vir) d[v]=d[u]+1,q.push(v);
		}
	}
	for (int i=0;i<cnt;i++) G[k][i]=d[pos[i]];
}
void solve(int n){
	memset(dp,63,sizeof(dp));
	dp[0][0]=0;
	for (int i=0;i<(1<<n);i++)
		for (int j=0;j<cnt;j++)
			if (dp[i][j]<1e9)
				for (int k=0;k<cnt;k++)
					if (G[j][k]<1e9){
						int t=i|T[pos[k]].end;
						dp[t][k]=min(dp[t][k],dp[i][j]+G[j][k]);
					}
	int ans=1e9,t=(1<<n)-1;
	for (int i=0;i<cnt;i++) ans=min(ans,dp[t][i]);
	printf("%d\n",ans);
}
int main(){
	while (~scanf("%d%d",&n,&m)){
		if (!n && !m) break;
		init(sum=0);
		for (i=0;i<n;i++) scanf("%s",s),insert(s,i);
		while (m--) scanf("%s",s),insert(s,-1);
		build();
		cnt=1;pos[0]=0;
		for (i=0;i<=sum;i++)
			if (T[i].end) pos[cnt++]=i;
		for (i=0;i<cnt;i++) path(i);
		solve(n);
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值