对所有模式串建立AC自动机。
每个单词结点要记录该单词长度。
然后在跑匹配的时候,对每个单词结点再处理3个值,代表可重叠的匹配次数,不可重叠的匹配次数,以及“上一次不可重叠的匹配位置”,这样结合单词长度就能保证不重叠。有多个重叠时,取靠前的位置更优。
Update:加了个优化,仅当某个结点的字符串的后缀可能是单词的时候,才会顺着fail指针往回跳。
#include<cstdio> #include<cstring> #include<queue> using namespace std; queue<int>q; bool word[601000]; int child[601000][26],fail[601000],size,pos[101000]; int cnta[601000],cntb[601000],posn[601000],lens[601000]; void Insert(char S[],int id) { int len=strlen(S); int now=0; for(int i=0;i<len;++i) { if(!child[now][S[i]-'a']) child[now][S[i]-'a']=size++; now=child[now][S[i]-'a']; } word[now]=1; lens[now]=len; pos[id]=now; } void build() { fail[0]=-1; q.push(0); while(!q.empty()) { int U=q.front(); q.pop(); for(int i=0;i<26;++i) if(child[U][i]) { int V=fail[U]; while(V!=-1) { if(child[V][i]) { fail[child[U][i]]=child[V][i]; break; } V=fail[V]; } if(V==-1) fail[child[U][i]]=0; if(word[fail[child[U][i]]]) word[child[U][i]]=1; q.push(child[U][i]); } } } void match(char S[]) { int len=strlen(S); int res=0,now=0; for(int i=0;i<len;++i) { if(child[now][S[i]-'a']) now=child[now][S[i]-'a']; else { int U=fail[now]; while(U!=-1 && child[U][S[i]-'a']==0) U=fail[U]; if(U==-1) now=0; else now=child[U][S[i]-'a']; } if(!word[now]) continue; int U=now; while(U) { if(word[U]) { ++cnta[U]; if(posn[U]<=i-lens[U]) { ++cntb[U]; posn[U]=i; } } U=fail[U]; } } } void Init() { memset(child,0,sizeof(child)); memset(fail,0,sizeof(fail)); memset(word,0,sizeof(word)); memset(pos,0,sizeof(pos)); memset(cnta,0,sizeof(cnta)); memset(cntb,0,sizeof(cntb)); memset(posn,-1,sizeof(posn)); memset(lens,0,sizeof(lens)); size=1; } char s[101000]; bool op[101000]; int n; int main() { char ss[9]; int T=0; //freopen("zoj3228.in","r",stdin); while(scanf("%s%d",s,&n)!=EOF) { printf("Case %d ",++T); Init(); for(int i=1;i<=n;++i) { scanf("%d%s",&op[i],ss); Insert(ss,i); } build(); match(s); for(int i=1;i<=n;++i) printf("%d ",op[i] ? cntb[pos[i]] : cnta[pos[i]]); puts(""); } return 0; }