题意:求一个文章(长度5.1e6)里面出现了多少个指定的模式串。重复出现只记一次。而且如果两个模式串都出现的情况下,一个是另一个的子串,则该子串不算出现过。

分析:AC自动机。

由于子串不算所以加一些特殊处理:

1.在文章匹配过程中,如果出现了一个模式串我们不是把匹配数量+1,而是记录那个出现过vis[id] = true;,当然trie树种也是记录了模式串的id。

2.在匹配结束后,我们遍历所有出现过的模式串,在Trie树种找到其所有出现过的子串并将其标为未出现过vis[id] = false;

要如何查找子串呢?

只需要记录每个出现过的串所对应的Trie树中的节点位置,由该节点向上走到root。其间走过的每个节点都沿着fail指针走到root一次。这样二重循环遍历到的所有节点就对应了Trie中所有该模式串的子串。

因为在AC自动机中父节点指针就是找前缀,fail指针就是找后缀。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cctype>
using namespace std; #define D(x)
const int MAX_LEN = (int)(5.1e6) + ;
const int MAX_N = ;
const int MAX_FINGER_LEN = ;
const int MAX_CHILD_NUM = ;
const int MAX_NODE_NUM = MAX_N * MAX_FINGER_LEN; int n;
char st[MAX_LEN];
char st2[MAX_LEN];
int vis[MAX_N];
bool check[MAX_NODE_NUM]; struct Trie
{
int next[MAX_NODE_NUM][MAX_CHILD_NUM];
int fail[MAX_NODE_NUM];
int count[MAX_NODE_NUM];
int father[MAX_NODE_NUM];
int node_cnt;
int root; void init()
{
node_cnt = ;
root = newnode();
} int newnode()
{
for (int i = ; i < ; i++)
next[node_cnt][i] = -;
count[node_cnt++] = ;
return node_cnt - ;
} int get_id(char a)
{
return a - 'A';
} void insert(char buf[], int index)
{
int len = strlen(buf);
int now = root;
for (int i = ; i < len; i++)
{
int id = get_id(buf[i]);
if (next[now][id] == -)
{
next[now][id] = newnode();
father[next[now][id]] = now;
}
now = next[now][id];
}
count[now] = index;
} void build()
{
queue<int>Q;
fail[root] = root;
father[root] = root;
for (int i = ; i < ; i++)
if (next[root][i] == -)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while (!Q.empty())
{
int now = Q.front();
Q.pop();
for (int i = ; i < ; i++)
if (next[now][i] == -)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
} int query(char buf[])
{
int now = root;
int res = ;
for (int i = ; buf[i]; i++)
{
now = next[now][get_id(buf[i])];
int temp = now;
while (temp != root && !check[temp])
{
if (count[temp] != )
vis[count[temp]] = temp;
check[temp] = true;
temp = fail[temp];
}
}
return res;
} void debug()
{
for(int i = ;i < node_cnt;i++)
{
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],count[i]);
for(int j = ;j < ;j++)
printf("%2d",next[i][j]);
printf("]\n");
}
} void cal()
{
for (int i = ; i <= n; i++)
{
if (vis[i] == )
{
continue;
}
int temp = vis[i];
while (temp != root)
{
int temp2 = temp;
while (temp2 != root && !check[temp2])
{
if (count[temp2] != && count[temp2] != i)
{
vis[count[temp2]] = ;
check[temp2] = true;
}
temp2 = fail[temp2];
}
temp = father[temp];
}
}
}
}; Trie ac; void transform(char st[], char st2[])
{
int len = ;
for (int i = ; st[i]; i++)
{
if (isupper(st[i]))
{
st2[len++] = st[i];
continue;
}
i++;
int temp = ;
while (isdigit(st[i]))
{
temp *= ;
temp += st[i] - '';
i++;
}
for (int j = ; j < temp; j++)
{
st2[len + j] = st[i];
}
len += temp;
i++;
}
st2[len] = ;
} void input()
{
scanf("%d", &n);
for (int i = ; i <= n; i++)
{
scanf("%s", st);
transform(st, st2);
ac.insert(st2, i);
}
} int work()
{
memset(vis, , sizeof(vis));
memset(check, , sizeof(check));
ac.query(st2);
memset(check, , sizeof(check));
ac.cal();
int ret = ;
for (int i = ; i <= n; i++)
{
if (vis[i])
{
D(printf("#%d\n", vis[i]));
ret++;
}
}
return ret;
} int main()
{
int t;
scanf("%d", &t);
while (t--)
{
ac.init();
input();
ac.build();
scanf("%s", st);
transform(st, st2);
printf("%d\n", work());
}
return ;
}

poj4052的更多相关文章

  1. poj4052 Hrinity

    pdf题面:传送门 题目大意:给定一些单词和一个句子,问有多少个单词在句子中出现过,如果一个但单词包含另一个单词,并且两个单词都出现过,那么只算最外层的单词(包含另一个单词的单词). 分析:这道题如果 ...

随机推荐

  1. Spring入门_03_构造注入

    实体类 Student.java package com.umgsai.spring.entity; import java.util.Date; public class Student { pri ...

  2. Oracle的DBMS_OUTPUT.PUT_LINE用法及脚本批处理方法

    打印至控制台(无显示): BEGIN DBMS_OUTPUT.PUT_LINE('Hey look, ma!'); END; / 打印至控制台(有显示): SET SERVEROUTPUT ON BE ...

  3. tomcat启动异常(严重: Dispatcher initialization failed Unable to load configuration. - [unknown location] )

    严重: Dispatcher initialization failed Unable to load configuration. - [unknown location] at com.opens ...

  4. bestcoder Round #7 前三题题解

    BestCoder Round #7 Start Time : 2014-08-31 19:00:00    End Time : 2014-08-31 21:00:00Contest Type : ...

  5. 使用jasmine来对js进行单元测试

    互联网的快速发展,给web开发人员带来了前所未有的挑战.对于前端开发,前端开发er所需要编写的js早已不是那些寥寥几行的视觉效果代码.代码量的大增,多人协同,人员素质悬殊不齐,这都需要一个标准,来对代 ...

  6. [转]Informatica vs SSIS

    转自 http://blog.csdn.net/thy822/article/details/8489779 这篇文章, 我不能同意更多, 所以转在这里. Here is my thinking af ...

  7. Javascript面向对象编程一:基础篇

    该随笔分为以下四部分: Javascript面向对象编程一:基础篇 Javascript面向对象编程二:封装 Javascript面向对象编程三:继承 Javascript面向对象编程四:控件 先弄个 ...

  8. springmvc 数据精准绑定

    因为使用dwz 的lookup功能,回调的值通过name以 xxx.xxValue 来自动得到,而我还有些表单数据的name是没有前缀的, 到springmvc后台绑定的的话默认的绑定是有问题的.这是 ...

  9. int long 等基础类型在不同平台的大小

    转自http://www.cnblogs.com/yishuiliunian/archive/2011/03/18/1988244.html 上次腾讯面试,问我int和long分别几个字节,结果被鄙视 ...

  10. Linux中获取本机网络信息的几个函数及应用

    一.读取/etc/hosts 几个函数 头文件<netdb.h> 1.void sethostent(int stayopen);//开打/etc/hosts 配置文件 2.struct ...