题意:
给你一个长串和n个短串,两种询问,可以覆盖(0)或者不可以覆盖(1) ,问出现次数。比如ababa 如果可以覆盖的话 aba出现次数是2 不可以覆盖的话出现次数是1 。
题解:
感恩我做了这道题,让我知道我以前的模板有点问题,ORZ,0的时候的话就是个AC自动机的模板,难就难在1的询问,1的询问的话就用一个变量在存放第一次出现这个短的串是在长串的什么位置,下次再出现的时候就用现在的位置减去之前的位置,查看是否大于该短的串的长度即可。
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN=1e5+7;
struct node
{
int id;
int len;
int last;
node *fail;
node *next[26];
node(){
last=-1;
fail=0;
id=0;
len=0;
for(int i=0;i<26;i++)
next[i]=0;
}
}*root;
int tot;
int map[MAXN];
int k[MAXN];
int cnt[MAXN][2];
void insert(char *s,int x)
{
node *r=root;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
if(r->next[x]==0) r->next[x]=new(node);
r=r->next[x];
}
if(r->id==0){
r->id=tot++;
}
map[x]=r->id;
r->len=len;
}
void getfail()
{
queue<node *>q;
q.push(root);
node *p;
node *temp;
while(!q.empty())
{
temp=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(temp->next[i])
{
if(temp==root)
{
temp->next[i]->fail=root;
}
else
{
p=temp->fail;
while(p)
{
if(p->next[i])
{
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==0)
temp->next[i]->fail=root;
}
q.push(temp->next[i]);
}
}
}
}
void ac_automation(char *s)
{
node *p=root;
int len=strlen(s);
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
while(!p->next[x]&&p!=root) p=p->fail;
p=p->next[x];
if(!p) p=root;
node *temp=p;
while(temp!=root)
{
if(temp->len>0)
{
cnt[temp->id][0]++;
if(temp->last==-1||i-temp->last>=temp->len)
{
cnt[temp->id][1]++;
temp->last=i;
}
}
// else 不能加这两个,之前的模板是有点问题的,要去掉这两行
// break;
temp=temp->fail;
}
}
}
void del(node *head)
{
for(int i=0;i<26;i++)
if(head->next[i])
del(head->next[i]);
delete(head);
}
int main()
{
char str[MAXN];
int n,Case=1;
while(~scanf("%s",str))
{
root=new(node);
tot=1;
memset(map,0,sizeof(map));
memset(k,0,sizeof(k));
memset(cnt,0,sizeof(cnt));
scanf("%d",&n);
char s[10];
for(int i=1;i<=n;i++)
{
scanf("%d%s",&k[i],s);
insert(s,i);
}
getfail();
ac_automation(str);
printf("Case %d\n",Case++);
for(int i=1;i<=n;i++)
{
printf("%d\n",cnt[map[i]][k[i]]);
}
printf("\n");
del(root);
}
}