• 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 table
    sql 保留两位小数+四舍五入
    easyui DataGrid 工具类之 util js
    easyui DataGrid 工具类之 后台生成列
    easyui DataGrid 工具类之 WorkbookUtil class
    easyui DataGrid 工具类之 TableUtil class
    easyui DataGrid 工具类之 Utils class
    easyui DataGrid 工具类之 列属性class
    oracle 卸载
    “云时代架构”经典文章阅读感想七
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4821674.html
Copyright © 2020-2023  润新知