poj_3987 Trie图
题目大意
有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;
}
转载于:https://www.cnblogs.com/gtarcoder/p/4821674.html
poj_3987 Trie图相关推荐
- hiho一下 第四周 Hihocoder #1036 : Trie图
#1036 : Trie图 时间限制:20000ms 单点时限:1000ms 内存限制:512MB 描述 前情回顾 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从 ...
- 【BZOJ-2938】病毒 Trie图 + 拓扑排序
2938: [Poi2000]病毒 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 609 Solved: 318 [Submit][Status][ ...
- hdu2457 Trie图+dp
hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...
- BZOJ 1444 [JSOI2009]有趣的游戏 (Trie图/AC自动机+矩阵求逆)
题目大意:给你$N$个长度相等且互不相同的模式串,现在有一个字符串生成器会不断生成字符,其中每个字符出现的概率是$p_{i}/q_{i}$,当生成器生成的字符串包含了某个模式串,则拥有该模式串的玩家胜 ...
- HiHocoder 1036 : Trie图 AC自动机
Trie图 先看一个问题:给一个很长很长的母串 长度为n,然后给m个小的模式串.求这m个模式串里边有多少个是母串的字串. 最先想到的是暴力O(n*m*len(m)) len(m)表示这m个模式串的平均 ...
- POJ 1625 Censored ( Trie图 DP 高精度 )
题意 : 给出 n 个单词组成的字符集 以及 p 个非法串,问你用字符集里面的单词构造长度为 m 的单词的方案数有多少种? 分析 : 与 POJ 2778 非常相似的一道题目,如果没有做过就尝试去了解 ...
- 【Trie图】Hiho4_Hihocoder
前情回顾 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从互联网上收集来的文章,和一本厚厚的河蟹词典,而他们要做的是判断这篇文章中是否存在那些属于河蟹词典中的词语. ...
- HDU 4511 小明系列故事——女友的考验 ( Trie图 DP )
题意 : 给出编号从1 ~ n 的 n 个平面直角坐标系上的点,求从给出的第一个点出发到达最后一个点的最短路径,其中有两种限制,其一就是只能从编号小的点到达编号大的点,再者不能走接下来给出的 m 个 ...
- [hiho 04]Trie图
题目描述 Trie 图就是在 Trie 树上建立 fail 指针,类似于KMP算法中的next数组的作用. 这个数据结构的作用是判断一个字符串中是否包含一组字符串中的任意一个. 结构体定义是这样的: ...
- BZOJ1444: [Jsoi2009]有趣的游戏(Trie图,矩乘)
Description Input 注意 是0<=P, n , l, m≤ 10. Output Sample Input input 1 3 2 2 1 2 1 2 AB BA AA inpu ...
最新文章
- Php7安装pdo_pgsql,pgsql扩展
- valid Palindrome -- leetcode
- themyleaf 图片上传_springboot thymeleaf 整合 百度富文本编辑器UEditor进行图片上传
- 基于北京二手房价数据的探索性数据分析和房价评估——房价评估模型构建
- 证件照处理工具,软件虽小,但是功能强大!(证件照工具,都用得上)
- 戴尔计算机没有硬盘驱动,戴尔电脑进PE系统找不到硬盘解决教程
- 小程序项目:基于微信小程序的便捷记账本系统——计算机毕业设计
- vs code git 编辑器中拉取(pull) 的时候报错 [rejected] v1.0.0 -> v1.0.0 (would clobber existing tag)
- Unity中的角色属性芒星比例图
- 在用AI死磕垃圾分类这件小事上,麻省理工、MaxAI们创造了这些利器,神助攻还是花拳绣腿?...
- 戴尔笔记本电脑怎下载c语言,戴尔笔记本电脑如何下载驱动
- lineageos没有信号解决方法
- Oracle table move tablespace
- 音频视频点播收费在线观看系统网站小程序app开发建设
- 外业调查工具助手,照片采集、精准定位、导航、地图查看
- 阿里云最新最全扩容方法
- flex布局以及实现垂直居中
- 律师要用到什么计算机技巧,【执业技巧】律师如何优雅地使用苹果电脑?这7款工具很有用...
- 史上最牛mysql-06 (多表连接)
- Linux配置无密登录