• 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 != ''){
    		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 != ''){
    		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 == '
    ')
    				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++] = '';
    		Search(gNodes + 1, gProgram, n);
    
    		printf("%d
    ", gVirusFindNum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    设计模式总结——程序猿武功秘籍(下一个)
    easyui datagrid显示进度条控制操作
    使用CountDownLatch和CyclicBarrier处理并发线程
    人类探索地外文明显著取得的进展
    Linux 启动过程的详细解释
    不会跳回到微博认定申请书
    unix域套接字UDP网络编程
    VS SQL 出现%CommonDir%dte80a.olb 该解决方案
    数据仓库与数据挖掘的一些基本概念
    CheckBoxPreference组件
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4821674.html
Copyright © 2020-2023  润新知