【HDU2896】
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2896
【分析】先输入一个N,代表病毒个数,再输入编号从1-N的N个病毒(20-200),在输入M个网站源代码(7000-10000),输出M个包含的病毒编号+含病毒网站总数。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
#define maxn 130 //孩子分支个数
#define maxm 10010 //网站字符最大长度
struct Node
{
int code; //病毒编号
int trien; //判断是否为单词结尾
Node *fail; //这个结点的fail指针
Node *next[maxn]; //这个结点的孩子结点
Node() //初始化
{
fail = NULL;
code = 0;
trien = 0;
memset(next, NULL, sizeof(next));
}
};
int sum[5]; //保存病毒编号。
void insert(Node *root, char *s, int index) //把病毒插入,然后生成一颗最小生成树,跟字典树差不多
{
Node *p = root;
int i = 0;
while (s[i])
{
int d = s[i];
if (p->next[d] == NULL)
p->next[d] = new Node();
p = p->next[d];
i++;
}
p->code = index; //这个结点作为单词的最后一个字符,存入病毒编号
p->trien = 1; //标记此结点为单词结尾
}
void build(Node *root) //建立每个一结点的fail结点
{
queue <Node *>q; //弄一个队列做bfs遍历
root->fail = NULL; //根节点的fail结点为NULL
q.push(root); //把根节点加入队列
while (!q.empty()) //队列为空的时候代表以及建立完成
{
Node *temp = q.front();
q.pop(); //取出队头temp,作为此时的父结点
Node *p = NULL; //待会需要跟着temp的fail去遍历
for (int i = 0; i<maxn; i++) //建立父结点temp孩子结点的fail
{
if (temp->next[i] != NULL) //父结点的此孩子存在
{
if (temp == root) //此时父结点是根节点的话
temp->next[i]->fail = root; //根结点的孩子的fail都指向根节点
else //父结点不是根结点
{
p = temp->fail; //让p指向父结点temp的fail
while (p != NULL) //当fail不为空
{
if (p->next[i] != NULL) //刚好父结点的fail结点有这个孩子
{
temp->next[i]->fail = p->next[i]; //让父结点temp的这个孩子结点的fail等于父结点跳转fail对应的孩子结点
break;
}
p = p->fail; //如果父结点的fail结点没有这个孩子,p就继续去找它的fail
}
if (p == NULL) //如果p为空,那么就代表p遍历到根节点
temp->next[i]->fail = root; //所以这个孩子的fail直接指向根节点
}
q.push(temp->next[i]); //这个孩子加入队列
}
}
}
}
int find(Node *root, char *s) //在串S种查找病毒
{
int cnt = 0; //答案数组下标
Node *p = root; //从病毒建立好的字典中的根节点开始找
int i = 0;
while (s[i])
{
int d = s[i];
while (p->next[d] == NULL && p != root) //当这个字符的孩子结点不存在,也不是根节点
p = p->fail; //就跳到父结点的fail继续找fail的fail,知道为根节点或者结点p的这个孩子结点存在
p = p->next[d]; //让这个结点指向这个字符孩子继续往下遍历。
if (p == NULL) //因为只有根节点的fail为空,所以又从根节点开始
p = root;
Node *temp = p;
while (temp != root && temp->code) //若temp不为根节点,并且病毒编号存在
{
sum[cnt] = temp->code; //存入编号
cnt += p->trien; //病毒个数增加
temp = temp->fail; //接着往这个结点的fail继续去遍历后面的字字符
}
i++;
}
return cnt;
}
int main()
{
int n, m;;
char str[205];
char web[maxm];
while (scanf("%d", &n) != -1)
{
Node*root = new Node();
for (int i = 1; i <= n; i++)
{
scanf("%s", str);
insert(root, str, i);
}
build(root);
int mang = 0;
scanf("%d", &m);
for (int i = 1; i <= m; i++)
{
scanf("%s", web);
int flag = 0;
int num = find(root, web);
if (num) //病毒个数不为0
{
flag = 1;
printf("web %d:", i);
sort(sum, sum + num); //把病毒序号排序
for (int j = 0; j<num; j++)
printf(" %d", sum[j]);
printf("\n");
}
if (flag == 1)
mang++; //有病毒的网站个数+1
}
printf("total: %d\n", mang);
}
return 0;
}
【HDU3065】
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3065
【分析】这个题目跟上面那个题目有点像,先输入存在的病毒,然后输入一串网站源代码,输出存在的病毒+这个病毒的个数,很大区别的是,病毒只有大写字母组成。
#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
#include<cstdio>
using namespace std;
const int maxn = 26;
const int maxx = 2000000 + 5;
int sum[1000 + 5];
char str[1000 + 5][50 + 5];
char s1[maxx], s2[maxx];
struct Node
{
int code;
Node *fail;
Node *next[maxn];
Node()
{
code = 0;
fail = NULL;
memset(next, NULL, sizeof(next));
}
};
void insert(Node *root, char *s, int index)
{
Node *p = root;
int i = 0;
while (s[i])
{
int d = s[i] - 'A';
if (p->next[d] == NULL)
p->next[d] = new Node();
p = p->next[d];
i++;
}
p->code = index;
}
void build(Node *root)
{
queue<Node *>q;
root->fail = NULL;
q.push(root);
Node *p = NULL;
while (!q.empty())
{
Node *temp = q.front();
q.pop();
for (int i = 0; i<maxn; i++)
{
if (temp->next[i] != NULL)
{
if (temp == root)
temp->next[i]->fail = root;
else
{
p = temp->fail;
while (p != NULL)
{
if (p->next[i] != NULL)
{
temp->next[i]->fail = p->next[i];
break;
}
p = p->fail;
}
if (p == NULL)
temp->next[i]->fail = root;
}
q.push(temp->next[i]);
}
}
}
}
void find(Node *root, char *s)
{
Node *p = root;
int i = 0;
while (s[i])
{
int d = s[i] - 'A';
while (p->next[d] == NULL && p != root)
p = p->fail;
p = p->next[d];
if (p == NULL)
p = root;
Node *temp = p;
while (temp != root && temp->code>0)
{
sum[temp->code]++; //这个编号对应的sum数组个数+1
temp = temp->fail;
}
i++;
}
}
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
Node *root = new Node();
memset(sum, 0, sizeof(sum));
for (int i = 1; i <= n; i++)
{
scanf("%s", str[i]);
insert(root, str[i], i);
}
build(root);
scanf("%s", s1);
int len = 0;
int l = strlen(s1);
for (int i = 0; i <= l; i++) //要等于字符串长度为什么呢,不知道怎么说 想想KMP的next数组就有感觉了。
{
if (s1[i] >= 'A' && s1[i] <= 'Z')
{
s2[len] = s1[i];
len++;
}
else
{
s2[len] = '\0';
find(root, s2);
len = 0;
}
}
for (int i = 1; i <= n; i++)
{
if (sum[i])
printf("%s: %d\n", str[i], sum[i]);
}
}
return 0;
}