HDU 2896 病毒侵袭

 这题 可用字典树做,也可用AC自动机做,需要注意的是,ASCII可见字符包括从33~126的字符,0~32 和127均为不可见字符(控制字符和换行,空格之类的)

所以在构造字典树的时候,可以让节点的子孩子数定义为  127 - 33 = 94

struct node{
    int number;  	//病毒编号 
    node * child[94]; //ASCII可见字符 	
	node(){	number = -1; memset(child, 0, sizeof(child)); } //构造函数
};

当然,直接定义为 node * child[128]也是能过的,不过内存耗的多点

前者:234MS 21980K2045 B G++

后者:234MS 29508K1951B G++


下面是用字典树做的代码:

// 题目:hdu_2896
// 地址:http://acm.hdu.edu.cn/showproblem.php?pid=2896
// 改孩子数前:2012-07-28 15:03:02	Accepted	2896	234MS	29508K	1951B	G++	Gneveek
// 改孩子数后: 2012-07-28 15:08:08	Accepted	2896	234MS	21980K	2045 B	G++	Gneveek
// 加if(sum == 3) break;后                      2896	140MS	21980K	3031 B   (code.len因为加注释而变长的)                        
// 127 - 33 = 94
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//字典树的结点 
struct node{
    int number;  	//病毒编号 
    node * child[94]; //ASCII可见字符 	
	node(){	number = -1; memset(child, 0, sizeof(child)); }
}* root = new node();  //定义全局的根结点 

char virus[205];	//病毒特征码 
char src_code[10005];
int ans[3];			//答案存到这里,因为题目说了最多只有3种病毒 

void add(char * str, int i);	//往字典树中加入数据,i是病毒编号 
int query(char * str);			//查询函数
 
int comp(const void *a, const void *b)
{//比较函数,qsort用的 
	return *(int *)a - *(int *)b;
}
int main(void)
{
	//freopen("E:\\input.txt", "r", stdin);
	int n, virus_num, i;
	//n用来读取输入数据中的两个数字	
	//virus_num用来存放源码中的病毒数量,该值由query函数返回
	//i是两个for的循环变量,因为VC奇葩的规定没写在里面
	 
	scanf("%d", &n);
	for(i=1; i<=n; i++)
	{//把病毒码加到字典树中去 
		scanf("%s", virus);
		add(virus, i);
	}
	
	scanf("%d", &n);
	int total = 0;	//total记录所有给出的网站中含有病毒的数量,用来在最后打印 
	for(i=1; i<=n; i++)
	{
		memset(ans, 0, sizeof(ans));	//init ans[] 
		scanf("%s", src_code);
		virus_num = query(src_code);	
		
		if(virus_num == 0)			//说明没病毒,不进行操作直接进入下一轮循环 
			continue;		
		else
		{
			qsort(ans, virus_num, sizeof(int), comp);  //先按升序排序,虽然最多只有3个 
			printf("web %d:", i);
			for(int j=0; j<virus_num; j++)			//打印 
				printf(" %d", ans[j]);
			puts("");
			total++;		//当前网站有病毒,total + 1 
		}		
	}
	printf("total: %d\n", total);
    return 0;
}

void add(char * str, int i)
{
	node * next = root;
    while(*str)
    {// -33是为了降低数据的规模,因为前32个字符都不可见,所以我们实际是从33开始的 
        if(next->child[*str-33] == NULL)        
            next->child[*str-33] = new node();
        
        next = next->child[*str-33];
        str++;
    }
	next->number = i;	//把病毒编号保存到末尾节点中去 
}

int query(char * str)
{
	//函数把查到的病毒码存到ans数组中,并返回总共的病毒数 
	//处理的方法就是从str的第一个字符开始扫描,定义一个tmp指针
	//每次都把tmp指向str指向的地方,然后操纵tmp,如果找到一个病毒
	//就把特征码存到ans数组中,然后让str指向当前tmp指向的地方
	//如果找不到,str++ 进行下一轮的搜索 
	
    node * next;
    char * tmp;
    int sum = 0;
	int i=0;  //ans数组下标 
    while(*str)
    {
    	if(sum == 3) 
			break;  //没加这句时时运行时间234ms 加完后140ms, 排行第5了 
        tmp = str;
        next = root;
        while(*tmp)
        {
            if(next->child[*tmp-33] != NULL)
                next = next->child[*tmp-33];
            else
                break;
			
			if(next->number != -1)
			{//说明当前结点是一个病毒的结尾了,把编号存到ans数组中			  
				ans[i++] = next->number; 
				str = tmp; //然后让str指向tmp指的地方,实际相当于str += (tmp-str); 
				sum++;
				break;
			}
			tmp++;
        }
        str++;
    }
    return sum;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值