您的位置:首页 > 其它

poj 4052 Hrinity 2012 金华邀请赛 AC自动机 DFA

2012-07-07 16:29 363 查看
/*
题意:
给定一些匹配串和一个模式串,问有多少个匹配串出现在模式串中。、
1.相同的匹配串记为一次
2.如果匹配串s1和s2都出现在模式串中,那么忽略s1
题解:
如果没有条件2,就是很经典的AC自动机题了。
按照经典方法我们可以找出所有出现过的匹配串,记为集合T,然后再这些串中去掉可以作为T中非自身子串的串
*/
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 type;
char ch[2505][1105];
int vis[3000],n;
const int kind=26;  //每个节点的子节点的个数上限

struct Node
{
int key,cnt;
struct Node *next[kind],*fail;
Node()
{
key=0;
for(int i=0;i<kind;i++)
next[i]=0;

}
}*root;

int Index(char c)
{
return c-'A';
}
void insert(char *s,int t)//构造字典树
{
int i=0;
Node *p=root;
while(s[i])
{
if(p->next[Index(s[i])]==0)
{
p->next[Index(s[i])]=new Node();
}
p=p->next[Index(s[i])];
i++;
}
p->key=1;//是字符串结尾记录为 1
p->cnt=t;//记录是第cnt号串的结尾
}

void build_fail()
{
root->fail=root;
queue<Node *> q;
q.push(root);
while(!q.empty())
{
Node *t=q.front();q.pop();
Node *p=0;
for(int i=0;i<kind;i++)
{
if(t->next[i]!=0)
{
if(t==root)  t->next[i]->fail = root;
else
{
p=t->fail; //从父节点的失败指针开始
while(p!=root&&p->next[i]==0) p=p->fail;//循环失败指针,一直搜索非根节点有相同索引的节点
if(p->next[i]) {
t->next[i]->fail=p->next[i];
} //找到,失败指针指向它
else  t->next[i]->fail=root;  //无相同索引,失败指针指向根节点
}
q.push(t->next[i]);
}
}
}
}
int bfs(char *s)//去掉s被标记过的字串
{
int i=0,sum=0,len=strlen(s);
Node *p=root;
while(s[i])
{

while(p->next[Index(s[i])]==0&&p!=root)
{
p=p->fail;
}//某个非根节点匹配失败,一直循环失败指针直到有某个字典树分支可以匹配
p=(p->next[Index(s[i])]==0)?root:p->next[Index(s[i])];//一直到根也未找到满足条件的分支,则从根开始匹配
Node *t=p;
// if(i==len-1)t=t->fail;
while(t!=root&&t->key!=-3)//循环失败指针,记录匹配次数
{
if(abs(t->key)==1&&vis[t->cnt])//是结尾,并且被标记则去掉标记
{
vis[t->cnt]=0;
}
t->key=-3;
t=t->fail;
}
i++;
}
}
int query1(char *s)
{
int i=0,sum=0;
Node *p=root;
while(s[i])
{
while(p->next[Index(s[i])]==0&&p!=root)
{
p=p->fail;
}//某个非根节点匹配失败,一直循环失败指针直到有某个字典树分支可以匹配
p=(p->next[Index(s[i])]==0)?root:p->next[Index(s[i])];//一直到根也未找到满足条件的分支,则从根开始匹配
Node *t=p;
while(t!=root&&t->key!=-4)//循环失败指针,记录匹配次数
{
if(abs(t->key)==1)
sum+=1;
t->key=-4;
t=t->fail;
}
i++;
}
return sum;
}
int query(char *s)
{
int i=0,sum=0;
Node *p=root;
//**************************找到所有出现在模式串中的匹配串
while(s[i])
{
while(p->next[Index(s[i])]==0&&p!=root)
{
p=p->fail;
}//某个非根节点匹配失败,一直循环失败指针直到有某个字典树分支可以匹配
p=(p->next[Index(s[i])]==0)?root:p->next[Index(s[i])];//一直到根也未找到满足条件的分支,则从根开始匹配
Node *t=p;
while(t!=root&&t->key>=0)//循环失败指针,记录匹配次数
{
if(t->key==1)//表示t->cnt号出现过,把标记改为-1,现在abs(key)=1表示是结尾
{
vis[t->cnt]=1;
t->key=-1;
}
else t->key=-2;//原来是0的改为-2,负数表示已经走过,这样可以节省时间
t=t->fail;
}
i++;
}
//**************************
for(int i=1;i<=n;i++)
if(vis[i])
{
bfs(ch[i]);//i号串出现过,去掉i号串所有的字串,包括自身
insert(ch[i],i);//把自身加回来
}
sum=query1(s);//再做一次模式串的匹配
return sum;
}
char str1[5100009],str2[5100009];

int main()
{
int m,tt,i,j;
scanf("%d", &tt);
while(tt--)
{
memset(vis,0,sizeof(vis));
root=new Node();
scanf("%d", &n);
for(int t=1;t<=n;t++)
{
scanf("%s", str1+1);
for(i=1, j=1; str1[i]; i++)
{
if(str1[i]>='A' && str1[i]<='Z') str2[j++] = str1[i];
else
{
i++;
int k = str1[i]-'0';i++;
while(str1[i]<'A' || str1[i]>'Z') k = k*10+ str1[i++]-'0';

while(k--) str2[j++] = str1[i];
i++;
}
}
str2[j] = '\0';
strcpy(ch[t],str2+1);
insert(str2+1,t);
}
build_fail();
scanf("%s", str1+1);
for(i=1, j=1; str1[i]; i++)
{
if(str1[i]>='A' && str1[i]<='Z') str2[j++] = str1[i];
else
{
i++;
int k = str1[i]-'0';i++;
while(str1[i]<'A' || str1[i]>'Z') k = k*10+ str1[i++]-'0';
while(k--) str2[j++] = str1[i];
i++;
}
}
str2[j] = '\0';
int ans = query(str2+1);
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: