题目地址: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;
}