题目大意

有N个病毒,病毒由A-Z字母构成,N个病毒各不相同。给出一段程序P,由A-Z字母构成,若病毒在在程序P或者P的逆转字符串P'中存在,则该程序P被该病毒感染。求出程序P被多少种病毒感染。

题目分析

典型的多模式串的字符串匹配问题,考虑使用Trie图。将M个待查的字符串作为模式串插入Trie图中,然后设置前缀指针,构造DFA。 
    判断程序P字符串翻转之后,是否含有某个模式串,一种方法是将P翻转,然后在DFA上查找;另一种是在构造DFA的时候,将模式串翻转,然后插入Trie图中,在匹配母串的时候就不需要将母串翻转了。 
    使用第二种方法需要注意的是,可能有两个模式串互为翻转。在Trie图的node节点中维护信息 pattern_index,若某节点为某个模式串的终止节点,则pattern_index为该模式串的序号(从1开始),若节点不是某个模式串的终止节点,则pattern_index = 0. 考虑两个模式串互为翻转(而且最多有两个模式串互为翻转)的情况,可以将pattern_index的高16bit作为pattern1的index,低16bit作为pattern2的index。

实现的时候,出现了几次超时。主要是重复访问了前缀指针节点。通过如下方法剪枝:

在trie图中遇到一个危险节点N(不一定为终止节点),此时母串遍历到当前位置P,可以确定在P之前,肯定出现了模式串 
    在N第一次被访问的时候,可以通过前缀指针找到N之前的所有模式串(需要不断的找prev,直到node到达根节点,比如 ABCDE中有模式串 BCDE, CDE, DE,需要不断的找前缀指针直到root,来防止遗漏某个模式串)

遇到危险节点N,向前找前缀指针的时候,碰到某个之前被访问过的节点A,即可返回.这是因为: 
    若A为危险节点,则它肯定在第一次被访问的时候就进行和N相同的处理(向前找模式串) 
    若A不是危险节点,在第一次被访问的时候,通过A的前缀指针,前缀指针的前缀指针....能到达的模式串都被找到了。因此之后再次碰到A,直接返回即可。

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define LETTERS 26
#define MAX_NODES 500000
#define MAX_VIRUS_LEN 1004
#define MAX_PROGRAM_LEN 5100005
#define MAX_VIRUS_NUM 255
char gProgram[MAX_PROGRAM_LEN];
bool gVirusVisited[MAX_VIRUS_NUM];
int gVirusFindNum;
int gVirusNum;
struct Node{
Node* childs[LETTERS];
Node* prev;
bool danger_node;
int pattern_index;
bool visited; //判断节点是否被访问过 //在trie图中遇到一个危险节点N(不一定为终止节点),此时母串遍历到当前位置P,可以确定在P之前,肯定出现了模式串
//在N第一次被访问的时候,可以通过前缀指针找到N之前的所有模式串
//(需要不断的找prev,直到node到达根节点,比如 ABCDE中有模式串 BCDE, CDE, DE,需要不断的找前缀指针直到root,来防止遗漏某个模式串) //遇到危险节点N,向前找前缀指针的时候,碰到某个之前被访问过的节点A,即可返回
//这是因为,若A为危险节点,则它肯定在第一次被访问的时候就进行和N相同的处理(向前找模式串)
//若A不是危险节点,在第一次被访问的时候,通过A的前缀指针,前缀指针的前缀指针....能到达的模式串都被找到了。因此之后
//再次碰到A,直接返回即可。
}; Node gNodes[MAX_NODES];
int gNodeCount;
void Insert(Node* root, char* str, int pat){
char*p = str;
Node* node = root;
while (*p != '\0'){
int index = *p - 'A';
if (node->childs[index] == NULL){
node->childs[index] = gNodes + gNodeCount++;
}
node = node->childs[index];
p++;
}
node->danger_node = true;
if (node->pattern_index == 0)
node->pattern_index = pat;
else{ //有可能两个virus串,互为逆串
node->pattern_index <<= 16;
node->pattern_index |= pat;
}
} void BuildDfa(){
Node* root = gNodes + 1;
for (int i = 0; i < LETTERS; i++){
gNodes[0].childs[i] = root;
}
root->prev = gNodes;
gNodes[0].prev = NULL;
queue<Node*> Q;
Q.push(root);
while (!Q.empty()){
Node* node = Q.front();
Q.pop();
Node* prev = node->prev;
Node* p;
for (int i = 0; i < LETTERS; i++){
if (node->childs[i]){
p = prev;
while (p && !p->childs[i]){
p = p->prev;
}
node->childs[i]->prev = p->childs[i];
if (p->childs[i]->danger_node)
node->childs[i]->danger_node = true;
Q.push(node->childs[i]);
}
}
}
} void FindPatternFromEndPoint(Node* node){
do{
if (node->visited) //若该节点之前被访问过,则直接返回
return; node->visited = true;
if (node->pattern_index){
if (node->pattern_index <= gVirusNum){
if (! gVirusVisited[node->pattern_index]){
gVirusVisited[node->pattern_index] = true;
gVirusFindNum++;
}
}
else{ //两个模式串互为逆串
int virus1 = node->pattern_index & 0xFFFF;
int virus2 = node->pattern_index >> 16;
if (!gVirusVisited[virus1]){
gVirusVisited[virus1] = true;
gVirusFindNum++;
}
if (!gVirusVisited[virus2]){
gVirusVisited[virus2] = true;
gVirusFindNum++;
}
}
}
node = node->prev;
} while (node->prev);
} void Search(Node* root, char* str, int n){
char*p = str;
Node* node = root;
while (*p != '\0'){
int index = *p - 'A';
if (gVirusFindNum >= n){
return;
}
while (node && node->childs[index] == NULL){
node = node->prev;
}
node = node->childs[index];
if (node->danger_node){
FindPatternFromEndPoint(node);
}
p++;
}
} int main(){
int cas;
scanf("%d", &cas);
char virus[MAX_VIRUS_LEN];
while (cas--){
int n;
memset(gNodes, 0, sizeof(gNodes));
gNodeCount = 2;
memset(gVirusVisited, false, sizeof(gVirusVisited));
gVirusFindNum = 0; scanf("%d", &n);
gVirusNum = n;
getchar();
for (int i = 0; i < n; i++){
scanf("%s", virus);
Insert(gNodes + 1, virus, i + 1);
reverse(virus, virus + strlen(virus));
Insert(gNodes + 1, virus, i + 1);
}
BuildDfa();
getchar();
char tmp;
int k = 0;
for (;;){
scanf("%c", &tmp);
if (tmp == '\n')
break; if (tmp != '['){
gProgram[k++] = tmp;
}
else{
int num;
scanf("%d", &num);
scanf("%c", &tmp);
for (int i = 0; i < num; i++){
gProgram[k++] = tmp;
}
scanf("%c", &tmp);
}
}
gProgram[k++] = '\0';
Search(gNodes + 1, gProgram, n); printf("%d\n", gVirusFindNum);
}
return 0;
}

poj_3987 Trie图的更多相关文章

  1. 【BZOJ-2938】病毒 Trie图 + 拓扑排序

    2938: [Poi2000]病毒 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 609  Solved: 318[Submit][Status][Di ...

  2. 【hihoCoder】1036 Trie图

    题目:http://hihocoder.com/problemset/problem/1036 给一个词典dict,词典中包含了一些单词words.要求判断给定的一个文本串text中是否包含这个字典中 ...

  3. 【hihoCoder 1036】Trie图

    看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动. $Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上 ...

  4. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  5. Trie图和Fail树

    Trie图和AC自动机的区别 Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了.这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯. 比如构造ne ...

  6. hdu2457 Trie图+dp

    hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...

  7. Trie图

    AC自动机是KMP的多串形式,当文本串失配时,AC自动机的fail指针告诉我们应该跳到哪里去继续匹配(跳到当前匹配串的最长后缀去),所以AC自动机的状态是有限的 但是AC自动机具有不确定性, 比如要求 ...

  8. CF 291E. Tree-String Problem [dfs kmp trie图优化]

    CF291E 题意:一棵树,每条边上有一些字符,求目标串出现了多少次 直接求目标串的fail然后一边dfs一边跑kmp 然后就被特殊数据卡到\(O(n^2)\)了... 因为这样kmp复杂度分析的基础 ...

  9. AC自动机相关Fail树和Trie图相关基础知识

    装载自55242字符串AC自动机专栏 fail树 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账- 有了这个东西, ...

随机推荐

  1. PKU OJ 1002 487-3279

    PKU OJ 1002 487-3279 487-3279 Description Businesses like to have memorable telephone numbers. One w ...

  2. HttpGet params not being sent httpget.setParams(params)不好使

    错误的代码 HttpClient httpclient = new DefaultHttpClient(); HttpUriRequest request = new HttpGet(uri); Ht ...

  3. CAS (3) —— Mac下配置CAS客户端经代理访问Tomcat CAS

    CAS (3) -- Mac下配置CAS客户端经代理访问Tomcat CAS tomcat版本: tomcat-8.0.29 jdk版本: jdk1.8.0_65 nginx版本: nginx-1.9 ...

  4. lua中table如何安全移除元素

    在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏. 引子 比如有些朋友常常这么做,大家看有啥问题 将test表中的偶数移除掉local test = ...

  5. 原来java中也有类似goto语句的标签啊--java label标签

    http://blog.sina.com.cn/s/blog_6d5354cd0100xjg7.html ——————————————————————————————————————————————— ...

  6. DBA不可不知的操作系统内核参数

    背景 操作系统为了适应更多的硬件环境,许多初始的设置值,宽容度都很高. 如果不经调整,这些值可能无法适应HPC,或者硬件稍好些的环境. 无法发挥更好的硬件性能,甚至可能影响某些应用软件的使用,特别是数 ...

  7. relation 关联模型

    关联关系必然有一个参照表,例如:有一个员工档案管理系统项目,这个项目要包括下面的一些数据表:基本信息表.员工档案表.部门表.项目组表.银行卡表(用来记录员工的银行卡资料).这些数据表之间存在一定的关联 ...

  8. Geometric deep learning on graphs and manifolds using mixture model CNNs

    Monti, Federico, et al. "Geometric deep learning on graphs and manifolds using mixture model CN ...

  9. 编写一个Filter,除继承HttpServlet类外还需要( )。

    A.继承Filter 类 B.实现Filter 接口 C.继承HttpFilter 类 D.实现HttpFilter接口 解答:B

  10. 有了malloc/free 为什么还要new/delete ?

    有了malloc/free 为什么还要new/delete ? malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符.它们都可 用于申请动态内存和释放 ...