题目大意
给出一个模式串集合和一个被匹配串,问被匹配串的前多少位可以由模式串组成。
思路
AC自动机。先根据给出的模式串建树,然后与被匹配串进行匹配。
建立一个T_len[]数组,T_len[i]==x表示以T[i]为首匹配的单词的最大长度为x。求出T_len[],就可以推导出答案,具体过程见solved()函数。
每次与模式串匹配成功时,都对T_len[]进行更新。当匹配结束,则T_len[]也更新完毕。
例如T[i]结尾的单词与长度为len的模式串匹配成功,则比较T_len[i-(len-1)]与len的大小,取两者中的较大值。其中i-(len-1)表示模式串在被匹配串中的匹配位置。
代码
/* ID: lujunda1 LANG: C++ PROG: prefix */ #include<iostream> #include<stdio.h> #include<cstring> using namespace std; #define T_SIZE 200000 #define P_SIZE 10 #define TOTAL_P 200 struct trie //利用结构体来封装字典树的节点。 { trie* next[26]; trie* fail; bool tail; //判断当前节点是否为某个单词的结尾。 int len; //节点在字典树中的“深度”。 trie() //构造函数。 { for(int i=0;i<26;i++) next[i]=NULL; fail=NULL; tail=false; len=0; } }; char T[T_SIZE+1]; char P[P_SIZE+1]; trie* q[TOTAL_P*P_SIZE]; int T_len[T_SIZE+1]; //T_len用于本题,记录连续区间。 //例:“T_len[i]==x”表示以T[i]为首匹配的单词的最大长度为x。 void insert(trie* root,char* s) //将字符串s所表示的单词插入到字典树中。 { trie* p=root; for(int i=0;s[i]!='\0';i++) { if(p->next[s[i]-'A']==NULL) { p->next[s[i]-'A']=new trie; p->next[s[i]-'A']->len=p->len+1; } p=p->next[s[i]-'A']; } p->tail=true; } void build_ac_automation(trie* root) //利用广搜构建失败指针 { int head=0,tail=0; q[tail++]=root; while(head!=tail) { trie* front=q[head++]; //front为队头元素 for(int i=0;i<26;i++) if(front->next[i]!=NULL) //遍历队头元素的子节点 { trie* p=front->fail; while(p!=NULL) //只有根节点的失败指针为NULL { if(p->next[i]!=NULL) //顺着失败指针往回走,直至某个节点,其拥有一个字母为'A'+i的子节点。 { front->next[i]->fail=p->next[i]; break; } p=p->fail; } if(p==NULL) //p==NULL说明顺着失败指针往回走的过程中没有找到合适的节点,所以将失败指针指向根节点。 front->next[i]->fail=root; q[tail++]=front->next[i]; } } } void ac_find(trie* root,char* T) { memset(T_len,0,sizeof(T_len)); trie* p=root; for(int i=0,len=strlen(T);i<len;i++) { while(p->next[T[i]-'A']==NULL&&p!=root) //若当前节点的没有一个字符为T[i]的儿子且当前节点不是根节点 //通俗的讲,就是顺着失败指针往回走,直至找到合适的节点或根节点为止。 p=p->fail; if(p->next[T[i]-'A']!=NULL) //p->next[T[i]-'A']==NULL说明没找到合适的节点,p指针指在根节点上。 p=p->next[T[i]-'A']; trie* temp=p; while(temp!=root) //顺着失败指针往回走,一直到根节点。 if(temp->tail) { if(T_len[i-(temp->len-1)]<temp->len) T_len[i-(temp->len-1)]=temp->len; break; } else temp=temp->fail; } } int solved() { int len=strlen(T),pos=-1; //pos表示最长连续区间在T中的结尾位置。 for(int i=0;i<=pos+1&&i<len;i++) if(T_len[i]+i-1>pos) pos=T_len[i]+i-1; return pos+1; } int main() { freopen("prefix.in","r",stdin); freopen("prefix.out","w",stdout); trie* root=new trie; while(scanf("%s",P)&&strcmp(P,".")) insert(root,P); build_ac_automation(root); T[0]='\0'; while(~scanf("%s",T+strlen(T))); ac_find(root,T); printf("%d\n",solved()); }