AC自动机。
给你一个长串 N个短的 短的可以覆盖(0)或者不可以覆盖(1) ,问出现次数。比如ababa 如果可以覆盖的话 aba出现次数是2 不可以覆盖的话出现次数是1 。
刚开始想得很纠结,觉得应该为所有可以覆盖的匹配一遍,然后给不可以覆盖的匹配一遍。。。
后来发现可以缩到一起。。因为不影响。
数据很阴险的有 比如
0 aba
0 aba
这种数据,第一种思路失败,第一种是每个字典树的节点存了两个值,一个是0 的时候的那个串的id,一个是1的时候的,这样的话,就不可以这么存了。
所以就用了一个指针数组,指向它对应的节点里的计数器(节点里两个计数器,分别记录0,1状态下的数量)。开始用的NODE的指针,后来发现没必要,int指针就好了。
计算不可以覆盖的时候,节点里存的是当前单词里最后出现的位置。。开始想错了 T T 。。小hh题解上这么说的。。。哎。。我还是应该多思考。。
把我的代码都改成静态了。。。挤进第一版。。
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <string>
#include <algorithm>
#define MID(x,y) ( ( x + y ) >> 1 )
#define L(x) ( x << 1 )
#define R(x) ( x << 1 | 1 )
#define BUG puts("here!!!")
#define STOP system("pause")
using namespace std;
const int CH_N = 27;
const int MAX = 100010;
const int MAX_NODE = MAX * 6;
struct NODE{
int id[2], len, pos;
NODE *next[CH_N], *fail;
void init()
{
pos = -1;
id[0] = id[1] = len = 0;
fail = NULL;
memset(next, 0, sizeof(next));
}
}*head;
NODE node[MAX_NODE];
int cnt;
int* y[MAX];
void Build_trie(char *s,NODE *head, int d,int x)
{
int len = strlen(s);
for(int i=0; i<len; i++)
{
int k = s[i] - 'a';
if( head->next[k] == NULL )
node[cnt].init(), head->next[k] = &node[cnt++];
head = head->next[k];
}
head->len = len;
y[x] = &(head->id[d]); // 每一个询问对应自己的计数器
}
queue<NODE*> q;
void Build_fail(NODE *head)
{
head->fail = NULL;
q.push(head);
while( !q.empty() )
{
NODE *now = q.front(); q.pop();
for(int i=0; i<CH_N; i++)
if( now->next[i] )
{
NODE *p = now->fail;
while( p )
{
if( p->next[i] )
{
now->next[i]->fail = p->next[i];
break;
}
p = p->fail;
}
if( p == NULL )
now->next[i]->fail = head;
q.push(now->next[i]);
}
}
}
void AC_find(NODE *head, char *s)
{
int len = strlen(s);
NODE* p = head;
for(int i=0; i<len; i++)
{
int k = s[i] - 'a';
while( p->next[k] == NULL && p != head )
p = p->fail;
p = p->next[k] == NULL ? head : p->next[k];
NODE *tmp = p;
while( tmp != head )
{
if( tmp->len > 0 )
{
tmp->id[0]++;
if( tmp->pos == -1 || i - tmp->pos >= tmp->len )
{
tmp->id[1]++;
tmp->pos = i; // 记录当前单词最后一次出现位置
}
}
tmp = tmp->fail;
}
}
}
char s[100005];
char word[10];
int main()
{
int n, ind = 1, d;
while( ~scanf("%s", s) )
{
cnt = 0;
node[cnt].init();
head = &node[cnt++];
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d %s", &d, word);
Build_trie(word, head, d, i);
}
Build_fail( head );
AC_find(head, s);
printf("Case %d\n", ind++);
for(int i=1; i<=n; i++)
printf("%d\n", *y[i]);
printf("\n");
}
return 0;
}