POJ 4052 Hrinity AC自动机 *

题目地址:http://poj.org/problem?id=4052

思路:

对终止节点定义两种操作:全忽略和半忽略
若a是终止节点,则全忽略a表示:
1)若a的父节点为根,则全忽略就是忽略a
2) 其他情况,全忽略a即为:
忽略a,并且全忽略a的前缀指针链上的全部终止节点
对a到根的路径上的所有危险节点ki,沿着ki的前缀指针
链全忽略所有终止节点(若ki是终止节点,则全忽略ki即可)
半忽略a:
在全忽略操作中,去掉“忽略a”,剩下的操作就是半忽略。


解法:

用母串S在trie图上遍历,走到未被忽略的终止节点x,则将x标记为已经匹配,并且半忽略x。

走到危险但非终止的节点y, 则沿着y的前缀指针链找到第一个终止节点x,将x标记为已经匹配,并且半忽略x。

统计匹配且未被忽略的模式串数目

设置标记,处理过的危险节点和终止节点就不用再处理。


AC代码如下:

/*
4
2
AB
DCB
DACB
3
A
AB
ABC
DABC
2
[2A]
[3A]B
[5A]B[4A]B
3
AB
CD
EF
ABCDEF

0
1
1
3

*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
#include<algorithm>
using namespace std;
const int letter=26;
char virus[2500+5];
char program[5100000+5];
struct Node{
	Node* pChilds[letter];
	Node* pPrev,*father;
	bool bBadNode,ignore,end,done,solved;//done 表示匹配 ,solved表示处理好了 
}tree[300000+10];
int nNode;
void Insert(Node* root,char* s)
{
	for(int i=0;s[i];i++)
	{
		if(root->pChilds[s[i]-'A']==NULL)
			root->pChilds[s[i]-'A']=tree+nNode++;
		root->pChilds[s[i]-'A']->father=root;
		root=root->pChilds[s[i]-'A'];
	}
	root->bBadNode=true;
	root->end=true;
}
void BuildDfa()
{
	for(int i=0;i<letter;i++)
		tree[0].pChilds[i]=tree+1;
	tree[1].pPrev=tree[1].father=tree;
	tree[0].pPrev=tree[0].father=NULL;
	queue<Node*> Q;
	Q.push(tree+1);
	while(!Q.empty())
	{
		Node* root=Q.front(); Q.pop();
		for(int i=0;i<letter;i++)
		{
			Node* p=root->pChilds[i];
			if(p==NULL) continue;
			Node* pPrev=root->pPrev;
			while(pPrev!=NULL){
				if(pPrev->pChilds[i]!=NULL){
					p->pPrev=pPrev->pChilds[i];
					if(p->pPrev->bBadNode) p->bBadNode=true;
					break;
				}
				else pPrev=pPrev->pPrev;
			}
			Q.push(p);
		}
	} 
}
void Ignore(Node* p)
{
	Node* pFath=p;
	while(pFath)
	{
		if(pFath->ignore);
		else if(pFath->end){
			if(pFath!=p) pFath->ignore=true;
		}
		else if(pFath->bBadNode){
			Node* pPrev=pFath;
			while(pPrev){
				if(pPrev->end&&pPrev!=p) pPrev->ignore=true;
				pPrev=pPrev->pPrev;
			}
		}
		pFath=pFath->father;
	}
}
void SearchDfa()
{
	Node* p=tree+1;
	for(int i=0;program[i];i++)
	{
		for(;;)
		{
			if(p->pChilds[program[i]-'A']!=NULL)
			{
				p=p->pChilds[program[i]-'A'];
				if(p->ignore||p->solved);
				else if(p->end){
					p->done=true;
					Ignore(p);
				}
				else if(p->bBadNode){
					Node* pPrev=p->pPrev;
					while(pPrev){
						if(pPrev->end){
							pPrev->done=true;
							Ignore(pPrev);
							break;
						}
						pPrev=pPrev->pPrev;
					}
				}
				p->solved=true;
				break;
			}	
			else p=p->pPrev;
		}	
	} 
}
void Read(char *s)
{
	int p=0,n;
	char ch,w;
	ch=getchar();
	while(ch=='\n') ch=getchar();
	do{
		if(ch==' ') continue;
		if(ch=='['){
			scanf("%d",&n);
			scanf("%c",&w);
			getchar();
			for(int i=0;i<n;i++) s[p++]=w;
		}
		else s[p++]=ch;
		ch=getchar();
	}while(ch!='\n');
	s[p++]='\0';
}
int main()
{
	int T,n; cin>>T;
	while(T--)
	{
		cin>>n;
		nNode=2;
		memset(tree,0,sizeof(tree));  
		for(int i=1;i<=n;i++)
		{
			Read(virus);
			Insert(tree+1,virus);
		}
		BuildDfa();
		Read(program); 
		SearchDfa();
		int ans=0;
		for(int i=2;i<nNode;i++)
		if(tree[i].done&&!tree[i].ignore) ans++;
		cout<<ans<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值