Poj 4052 Hrinity (金华邀请赛I)(字符串_AC自动机)

本文介绍了一道关于AC自动机的应用题目,通过构建字典树解决字符串匹配问题,并特别处理了子串计数的特殊情况。提供了详细的解题步骤及核心代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://poj.org/problem?id=4052


题目大意北大还没把题目放出来,大意是给定n个字符串和一篇文章,求这些字符串中有几个在文章,如果某些字符串是某个字符串的字符串,那合起来只能算一个。最后输出含有的个数。


解题思路:这题在参加现场赛时根本就没看,不过当时也不一定做的出来,毕竟半年没做ac自动机了。这两天花了些时间把以前比赛未过的题目做了下,不做感觉那些被虐过的比赛还会一次一次的重演。凌晨时刻,人总是十分风骚,不说了,下面是思路:本题抛开含有子串这个约束就是裸ac自动机,现在有了这个约束可以三步走:1、把畸形字符串转化为正常字符串,题目有保证不管是畸形的还是正常的长度都有上限,用O(n)的复杂度转换下后面就简单了。2、建字典树,转换成Trie图,并把出现的串标记出来 3、利用Trie图把子串的标记去掉,最后统计下个数。

     发现很多人写解题报告只贴代码,不给思路不给数据,看的我蛋疼,我就大发慈悲的给一坨数据吧。


测试数据:

100
8
B
CB
ACB
DACB
D
DA
DAC
DACB
DACB


3
A
B
C
ABCC


2
[2A]B
[3A]B
[5A]B[4A]B


2
AB
DCB
DABC


3
A
AB
ABC
DABC


3
AB
CD
EF
ABCDEF


26
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
ABCDEFGHIJKLMNOPQRSTUVWXYZ


代码:

#include <stdio.h>
#include <string.h>
#define MIN 6200000
#define MAX 4000000


struct node {

	int flag,in;
	node *fail,*next[26],*fa;
}*qu[MAX],arr[MAX],*root;
int  n,m,ans,total,vis[3000];
char dir[2600][1200],temp[MIN],str[MIN];


node *CreateNode() {

	node *p = &arr[total++];
	p->flag = 0;
	p->fail = p->fa = NULL;
	for (int i = 0; i < 26; ++i)
		p->next[i] = NULL;
	return p;
}
void Insert(char *dir,int now) {

	int i = 0,k;
	node *p = root,*temp;


	while (dir[i]) {

		k = dir[i++] - 'A';
		if (p->next[k] == NULL)
			p->next[k] = CreateNode();
		temp = p;
		p = p->next[k];
		p->fa = temp;
	}
	p->flag = 1,p->in = now;
}
void Build_AC() {

	int head,tail,i;
	head = tail = 0;
	root->fail = root;
	root->fa   = root;
	qu[++head] = root;


	while (tail < head) {

		node *p = qu[++tail];
		for (i = 0; i < 26; ++i)
			if (p->next[i] != NULL) {

				if (p == root) p->next[i]->fail = root;
				else p->next[i]->fail = p->fail->next[i];
				qu[++head] = p->next[i];
			}
			else {
				
				if (p == root) p->next[i] = root;
				else p->next[i] = p->fail->next[i];
			}
	}
}
int Query_1A(char *str) {

	int i,j,k;
	i = ans = 0;
	node *p = root,*temp;


	while (str[i]) {

		k = str[i++] - 'A';
		p = p->next[k];
		if (p->flag) vis[p->in] = 1;
	}


	for (i = 0; i < n; ++i) if (vis[i]) {

		j = 0,p = root;
		while (dir[i][j]) {

			k = dir[i][j++] - 'A';
			p = p->next[k];
		}
		temp = p->fa;
		while (temp != root) {

			if (temp->flag) {

				vis[temp->in] = 0;
				temp->flag = 0;
			}
			temp = temp->fa;
		}
		temp = p->fail;
		while (temp != root) {

			if (temp->flag) {

				vis[temp->in] = 0;
				temp->flag = 0;
			}
			temp = temp->fail;
		}
	}


	for (i = 0; i < n; ++i)
		if (vis[i]) ans++;
	return ans;
}
void Decode(char *str){

	int i = 0,j = 0,q,k,tpk;


	while (str[i]) {

		if (str[i] != '[')
			temp[j++] = str[i++];
		else {
			
			for (q = 0,k = i + 1; str[k] >= '0' && str[k]<= '9'; ++k)
					q = q * 10 + str[k] - '0';
			for (tpk = 1; tpk <= q; ++tpk)
				temp[j++] = str[k];
			i = k + 2;
		}
	}
	temp[j] = '\0';
	strcpy(str,temp);
}


int main()
{
	int i,j,k,t;

	
	scanf("%d",&t);
	while (t--) {

		total = 0;
		root = CreateNode();
		memset(vis,0,sizeof(vis));
		scanf("%d",&n);
		for (i = 0; i < n; ++i) {

			scanf("%s",dir[i]);
			Decode(dir[i]),Insert(dir[i],i);
		}


		scanf("%s",str);
		Decode(str);
		
		
		Build_AC();
		ans = Query_1A(str);
		printf("%d\n",ans);
	}
	return 0;
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值