对所有模式串建立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\n",++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\n",op[i] ? cntb[pos[i]] : cnta[pos[i]]);
puts("");
}
return 0;
}