zoj 3228 Searching the String

AC自动机。

 给你一个长串 N个短的 短的可以覆盖(0)或者不可以覆盖(1) ,问出现次数。比如ababa  如果可以覆盖的话 aba出现次数是2  不可以覆盖的话出现次数是1 。
刚开始想得很纠结,觉得应该为所有可以覆盖的匹配一遍,然后给不可以覆盖的匹配一遍。。。
后来发现可以缩到一起。。因为不影响。
数据很阴险的有 比如
0 aba
0 aba
这种数据,第一种思路失败,第一种是每个字典树的节点存了两个值,一个是0 的时候的那个串的id,一个是1的时候的,这样的话,就不可以这么存了。
所以就用了一个指针数组,指向它对应的节点里的计数器(节点里两个计数器,分别记录0,1状态下的数量)。开始用的NODE的指针,后来发现没必要,int指针就好了。
计算不可以覆盖的时候,节点里存的是当前单词里最后出现的位置。。开始想错了 T T 。。小hh题解上这么说的。。。哎。。我还是应该多思考。。
把我的代码都改成静态了。。。挤进第一版。。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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; inext[k] == NULL )
			node[cnt].init(), head->next[k] = &node[cnt++];
		head = head->next[k];
	}
	head->len = len;
	y[x] = &(head->id[d]);	// 每一个询问对应自己的计数器 
}

queue 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; inext[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; inext[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;
}


你可能感兴趣的:(AC自动机,字符串相关,zoj)