Problem A.Keywords Search
d.n个关键字,1段描述,求描述中出现了多少关键字
s.
c.
/* ac自动机模板 n个关键字,1段描述,求描述中出现了多少关键字 */ #include<iostream> #include<stdio.h> #include<string.h> #include<queue> using namespace std; struct Trie{ int next[500010][26],fail[500010],end[500010]; int root,L; int newnode(){ for(int i=0;i<26;i++) next[L][i]=-1; end[L++]=0; return L-1; } void init(){ L=0; root=newnode(); } void insert(char buf[]){ int len=strlen(buf); int now=root; for(int i=0;i<len;i++){ if(next[now][buf[i]-'a']==-1) next[now][buf[i]-'a']=newnode(); now=next[now][buf[i]-'a']; } end[now]++; } void build(){ queue<int>Q; fail[root]=root; for(int i=0;i<26;i++) if(next[root][i]==-1) next[root][i]=root; else{ fail[next[root][i]]=root; Q.push(next[root][i]); } while(!Q.empty()){ int now=Q.front(); Q.pop(); for(int i=0;i<26;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else{ fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } int query(char buf[]){ int len=strlen(buf); int now=root; int res=0; for(int i=0;i<len;i++){ now=next[now][buf[i]-'a']; int temp=now; while(temp!=root){ res+=end[temp]; end[temp]=0; temp=fail[temp]; } } return res; } void debug(){ for(int i=0;i<L;i++){ printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j=0;j<26;j++) printf("%2d",next[i][j]); printf("] "); } } }; char buf[1000010]; Trie ac; int main(){ int T; int n; scanf("%d",&T); while(T--){ scanf("%d",&n); ac.init(); for(int i=0;i<n;i++){ scanf("%s",buf); ac.insert(buf); } ac.build(); scanf("%s",buf); printf("%d ",ac.query(buf)); } return 0; }
Problem B.病毒侵袭
d.这题和上题差不多,要输出出现模式串的id,用end记录id就可以了。还有trie树的分支是128的
s.
c.
/* ac自动机模板 n个关键字,1段描述,求描述中出现了多少关键字 */ #include<iostream> #include<stdio.h> #include<string.h> #include<queue> #include<set> using namespace std; struct Trie{ int next[510*210][128],fail[510*210],end[510*210]; int root,L; int newnode(){ for(int i=0;i<128;i++) next[L][i]=-1; end[L++]=-1; return L-1; } void init(){ L=0; root=newnode(); } void insert(char buf[],int id){ int len=strlen(buf); int now=root; for(int i=0;i<len;i++){ if(next[now][buf[i]]==-1) next[now][buf[i]]=newnode(); now=next[now][buf[i]]; } end[now]=id; } void build(){ queue<int>Q; fail[root]=root; for(int i=0;i<128;i++) if(next[root][i]==-1) next[root][i]=root; else{ fail[next[root][i]]=root; Q.push(next[root][i]); } while(!Q.empty()){ int now=Q.front(); Q.pop(); for(int i=0;i<128;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else{ fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } set<int>myset; bool query(char buf[],int id){ myset.clear(); int len=strlen(buf); int now=root; for(int i=0;i<len;i++){ now=next[now][buf[i]]; int temp=now; while(temp!=root){ if(end[temp]!=-1){ myset.insert(end[temp]); } temp=fail[temp]; } } if(!myset.empty()){ printf("web %d:",id); for(set<int>::iterator it=myset.begin();it!=myset.end();++it){ printf(" %d",*it); } printf(" "); return true; } return false; } void debug(){ for(int i=0;i<L;i++){ printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j=0;j<128;j++) printf("%2d",next[i][j]); printf("] "); } } }; char buf[10005]; Trie ac; int main(){ int N,M; int ans; while(~scanf("%d",&N)){ ac.init(); for(int i=1;i<=N;++i){ scanf("%s",buf); ac.insert(buf,i); } ac.build(); scanf("%d",&M); ans=0; for(int i=1;i<=M;++i){ scanf("%s",buf); if(ac.query(buf,i)){ ++ans; } } printf("total: %d ",ans); } return 0; }
Problem C.病毒侵袭持续中
d.这题的变化也不大,就是需要输出每个模式串出现的次数,查询的时候使用一个数组进行记录就可以了
s.
c.
/* ac自动机模板 n个关键字,1段描述,求描述中出现了多少关键字 */ #include<iostream> #include<stdio.h> #include<string.h> #include<queue> using namespace std; char str[1024][64]; struct Trie{ int next[1005*55][26],fail[1005*55],end[1005*55]; int root,L; int newnode(){ for(int i=0;i<26;i++) next[L][i]=-1; end[L++]=-1; return L-1; } void init(){ L=0; root=newnode(); } void insert(char buf[],int id){ int len=strlen(buf); int now=root; for(int i=0;i<len;i++){ if(next[now][buf[i]-'A']==-1) next[now][buf[i]-'A']=newnode(); now=next[now][buf[i]-'A']; } end[now]=id; } void build(){ queue<int>Q; fail[root]=root; for(int i=0;i<26;i++) if(next[root][i]==-1) next[root][i]=root; else{ fail[next[root][i]]=root; Q.push(next[root][i]); } while(!Q.empty()){ int now=Q.front(); Q.pop(); for(int i=0;i<26;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else{ fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } int sum[1024]; void query(char buf[],int N){ memset(sum,0,sizeof(sum)); int len=strlen(buf); int now=root; for(int i=0;i<len;i++){ if(buf[i]<'A'||buf[i]>'Z'){ now=root;//如果用128个来建字典树的话,没有的字符应该是指向root吧?应该是。 continue;//(即下面的now=next[now][buf[i]]的值应该是root?) //使用这种判断的开辟26个字符即可,节省空间 } now=next[now][buf[i]-'A']; int temp=now; while(temp!=root){ if(end[temp]!=-1){ ++sum[end[temp]]; } temp=fail[temp]; } } for(int i=0;i<N;++i){ if(sum[i]>0){ printf("%s: %d ",str[i],sum[i]); } } } void debug(){ for(int i=0;i<L;i++){ printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j=0;j<26;j++) printf("%2d",next[i][j]); printf("] "); } } }; char buf[2000005]; Trie ac; int main(){ int N; while(~scanf("%d",&N)){ ac.init(); for(int i=0;i<N;i++){ scanf("%s",str[i]); ac.insert(str[i],i); } ac.build(); scanf("%s",buf); ac.query(buf,N); } return 0; }