- 可重叠与不可重叠匹配
比如模式串为aba,字符串为abababab,若可重叠匹配,那么aba出现的次数为三次;若为不可重叠匹配,那么出现的次数为两次: aba / b / aba /b
AC自动机对于可重叠匹配很方便,直接顺着节点的fail指针一直走就可以.
不可重叠匹配
Searching the String
题意:
有若干个模式串,然后在字符串中查询它们出现的次数.但是0表示可以重叠出现,1表示不可以重叠出现;
题解:
假设字典树的每个单词尾节点都有个变量为pos,记录最后一次匹配主串的位置;
对于可重叠匹配,直接顺着自动机的fail指针一直走;
对于不可重叠匹配,如果当前正在匹配主串的位置-最后一次成功匹配的字符串的位置大于等于单词的长度,那么这样的匹配是可行的;
比如模式串为aba,字符串为abababab,第一次匹配到aba的位置为2;第二次匹配到aba位置为4,但是4-2<3,所以这次的匹配是无效的;第三次匹配到aba位置为6,6-2=4>=3,成功匹配;所以最后成功匹配次数为2
/* 这题数据有点恶心,模式串会重复出现
input:
ab
4
0 ab
1 ab
0 ab
1 ab
output:
Case 1
1
1
1
1
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int MAXN=100010;
const int BASE=26;
struct Node
{
int fail;
int next[26];
bool ed;
int len;
int id1;
int id2;
int pos;
void init()
{
fail=0;
ed=false;
len=pos=-1;
id1=id2=0;
memset(next,0,sizeof(next));
}
};
Node trie[600005];
int trie_s;
int *x[MAXN];
char str[MAXN];
void put(char *str,int choose,int id)
{
int p=1;
int len=strlen(str);
for(int i=0;i<len;i++)
{
int pos=str[i]-'a';
if(trie[p].next[pos]==0)
{
trie[p].next[pos]=++trie_s;
trie[trie_s].init();
}
p=trie[p].next[pos];
}
trie[p].ed=true;
trie[p].len=len;
if(choose==0)
{
x[id]=&(trie[p].id1);
}
else x[id]=&(trie[p].id2);
}
void getFail()
{
queue<int> que;
int son,p=1,temp;
que.push(p);
while(!que.empty())
{
int curr=que.front();
que.pop();
for(int i=0;i<26;i++)
{
son=trie[curr].next[i];
if(son)
{
if(curr==1) trie[son].fail=1;
else
{
temp=trie[curr].fail;
while(temp!=0)
{
if(trie[temp].next[i])
{
trie[son].fail=trie[temp].next[i];
break;
}
temp=trie[temp].fail;
}
if(temp==0) trie[son].fail=1;
}
que.push(son);
}
}
}
}
void query(char *str)
{
int len=strlen(str);
int p=1,temp;
for(int i=0;i<len;i++)
{
int pos=str[i]-'a';
while(!trie[p].next[pos]&&p!=1) p=trie[p].fail;
p=trie[p].next[pos];
if(p==0) p=1;
temp=p;
while(temp!=1)
{
if(trie[temp].ed)
{
trie[temp].id1=trie[temp].id1+1;
if(trie[temp].pos==-1||(i-trie[temp].pos>=trie[temp].len))
{
trie[temp].id2=trie[temp].id2+1;
trie[temp].pos=i;
}
}
temp=trie[temp].fail;
}
}
}
int main()
{
int n,choose,cas=1;
char ss[10];
while(scanf("%s",str)!=EOF)
{
scanf("%d",&n);
trie[1].init();
trie_s=1;
for(int i=1;i<=n;i++)
{
scanf("%d %s",&choose,ss);
put(ss,choose,i);
}
getFail();
query(str);
printf("Case %d\n",cas++);
for(int i=1;i<=n;i++)
{
printf("%d\n",*x[i]);
}
printf("\n");
}
return 0;
}