这题 可用字典树做,也可用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;
}