AC自动机---Searching the String
ZOJ 3228
题目网址:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16401
Description
Little jay really hates to deal with string. But moondy likes it very much, and she's so mischievous that she often gives jay some dull problems related to string. And one day, moondy gave jay another problem, poor jay finally broke out and cried, " Who can help me? I'll bg him! "
So what is the problem this time?
First, moondy gave jay a very long string A. Then she gave him a sequence of very short substrings, and asked him to find how many times each substring appeared in string A. What's more, she would denote whether or not founded appearances of this substring are allowed to overlap.
At first, jay just read string A from begin to end to search all appearances of each given substring. But he soon felt exhausted and couldn't go on any more, so he gave up and broke out this time.
I know you're a good guy and will help with jay even without bg, won't you?
Input
Input consists of multiple cases( <= 20 ) and terminates with end of file.
For each case, the first line contains string A ( length <= 10^5 ). The second line contains an integer N ( N <= 10^5 ), which denotes the number of queries. The next N lines, each with an integer type and a string a ( length <= 6 ), type = 0 denotes substring a is allowed to overlap and type = 1 denotes not. Note that all input characters are lowercase.
There is a blank line between two consecutive cases.
Output
For each case, output the case number first ( based on 1 , see Samples ).
Then for each query, output an integer in a single line denoting the maximum times you can find the substring under certain rules.
Output an empty line after each case.
Sample Input
ab 2 0 ab 1 ab abababac 2 0 aba 1 aba abcdefghijklmnopqrstuvwxyz 3 0 abc 1 def 1 jmn
Sample Output
Case 1 1 1 Case 2 3 2 Case 3 1 1 0
Hint
In Case 2,you can find the first substring starting in position (indexed from 0) 0,2,4, since they're allowed to overlap. The second substring starts in position 0 and 4, since they're not allowed to overlap.
For C++ users, kindly use scanf to avoid TLE for huge inputs.
题意:给了N个模式串,然后又给了一个长串,求每个模式串在这个长串中出现的次数。每个模式串前给了类型限制,若为0,表示串可以重叠,1表示不能重叠。
思路:当为0类型时,可以重叠,则和以前的AC自动机模板一样,1时,需要在模式串的最后一个字符的结构体内标识一下上一次这个串在长串中出现的位置,当再次匹配到这个串的末尾时,用当前串的位置序号减去上次出现的位置序号,若长大于等于模式串的长度,则串出现次数加一。注意:相同的串并且是相同类型的串可能出现多次,如 0 aba ,0 aba
所以可以在字符结构体中记录0型和1型串出现次数,记录输入的模式串,最后按顺序输出时,将对应的串和类型数传到trie树中查找,返回输出记录0型和1型串出现的次数。
参考别人的代码如下:
#include <stdio.h> #include <string.h> #include <memory.h> struct node{ node *fail; node *next[26]; int id; node(){ fail=NULL; id=0; memset(next,NULL,sizeof(next)); } }*q[770000],*root; int head,tail; char str1[100003][7],str2[100003]; int A[100003],cnt[100003][2],pos[100003],Len[100003],n; void insert_Trie(char *str,int num1){ node *p=root; int i=0,id; while(str[i]){ id=str[i]-'a'; if(p->next[id]==NULL) p->next[id]=new node(); p=p->next[id];i++; } p->id=num1; } int search_1(char *str){ node *p=root; int m,i=0; while(str[i]){ m=str[i]-'a'; if(p->next[m]==NULL) return -1; p=p->next[m]; i++; } return p->id; } void setfail() ///初始化fail指针,BFS { q[tail++]=root; while(head!=tail) { node *p=q[head++]; node *temp=NULL; for(int i=0;i<26;i++) if(p->next[i]!=NULL) { if(p==root) ///首字母的fail必指向根 p->next[i]->fail=root; else { temp=p->fail; ///失败指针 while(temp!=NULL) ///2种情况结束:匹配为空or找到匹配 { if(temp->next[i]!=NULL) ///找到匹配 { p->next[i]->fail=temp->next[i]; break; } temp=temp->fail; } if(temp==NULL) ///为空则从头匹配 p->next[i]->fail=root; } q[tail++]=p->next[i]; ///入队 } } } void query(){ int i=0; node *p=root,*temp; while(str2[i]){ int id=str2[i]-'a'; while(p->next[id]==NULL&&p!=root) p=p->fail; p=p->next[id]; p=(p==NULL)?root:p; temp=p; while(temp!=root){ if(temp->id){ cnt[temp->id][0]++; } temp=temp->fail; } i++; } } void query1(){ int i=0; node *p=root,*temp; while(str2[i]){ int id=str2[i]-'a'; while(p->next[id]==NULL&&p!=root) p=p->fail; p=p->next[id]; p=(p==NULL)?root:p; temp=p; while(temp!=root){ if(temp->id&&i-pos[temp->id]>=Len[temp->id]){ pos[temp->id]=i; cnt[temp->id][1]++; } temp=temp->fail; } i++; } } int query_num(char *str,int aa){ int i=0; node *p=root; while(str[i]){ int id=str[i]-'a'; p=p->next[id]; i++; } return cnt[p->id][aa]; } void del(node *p){ if(p==NULL)return ; for(int i=0;i<26;i++)del(p->next[i]); delete p; } int main(){ int t=1; while(scanf("%s",str2)!=-1){ scanf("%d",&n); head=0; tail=0; root=new node(); memset(cnt,0,sizeof(cnt)); memset(pos,-1,sizeof(pos)); for(int i=1;i<=n;i++){ scanf("%d%s",&A[i],str1[i]); Len[i]=strlen(str1[i]); insert_Trie(str1[i],i); } setfail(); query(); query1(); printf("Case %d\n",t++); for(int i=1;i<=n;i++){ int ttt=query_num(str1[i],A[i]); printf("%d\n",ttt); } printf("\n"); del(root); } return 0; }
我的代码如下:(我写的代码很清晰,各种样例都测试通过了,但提交就是wa,唉~)
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define N 770010 char str[110010],keyword[110010][10]; int head,tail,key[110010]; struct node { node *fail; node *next[26]; int f; int count1; int count2; int b,l; node() { fail=NULL; count1=0; count2=0; f=-1; b=0; l=0; for(int i=0;i<26;i++) next[i]=NULL; } }*q[N]; node *root; int insert(char *str,int x) ///建立Trie { int temp,len; node *p=root; len=strlen(str); for(int i=0;i<len;i++) { temp=str[i]-'a'; if(p->next[temp]==NULL) p->next[temp]=new node(); p=p->next[temp]; } p->f++; p->l=len; if(!x) return p->count1; else return p->count2; } void setfail() ///初始化fail指针,BFS { q[tail++]=root; while(head!=tail) { node *p=q[head++]; node *temp=NULL; for(int i=0;i<26;i++) if(p->next[i]!=NULL) { if(p==root) ///首字母的fail必指向根 p->next[i]->fail=root; else { temp=p->fail; ///失败指针 while(temp!=NULL) ///2种情况结束:匹配为空or找到匹配 { if(temp->next[i]!=NULL) ///找到匹配 { p->next[i]->fail=temp->next[i]; break; } temp=temp->fail; } if(temp==NULL) ///为空则从头匹配 p->next[i]->fail=root; } q[tail++]=p->next[i]; ///入队 } } } void query() { int index,len; node *p=root; len=strlen(str); for(int i=0;i<len;i++) { index=str[i]- 'a'; while(p->next[index]==NULL&&p!=root) ///跳转失败指针 p=p->fail; p=p->next[index]; if(p==NULL) p=root; node *temp=p; ///p不动,temp计算后缀串 while(temp!=root&&temp->f!=-1) { temp->count1++; if(temp->b==0||(i-temp->b)>=(temp->l)) { temp->count2++; temp->b=i; } temp=temp->fail; } } } void free_(node *r) { for(int i=0; i<26; i++) { if(r->next[i]) free_(r->next[i]); } free(r); } int main() { int num,Case=1; while(~scanf("%s",str)) { head=tail=0; memset(key,0,sizeof(key)); root = new node(); scanf("%d", &num); for(int i=1;i<=num;i++) { scanf("%d %s",&key[i],keyword[i]); insert(keyword[i],i); } setfail(); query(); printf("Case %d\n",Case++); for(int i=1;i<=num;i++) { printf("%d\n",insert(keyword[i],key[i])); } printf("\n"); free_(root); } return 0; }