HDU2896和HDU3065【AC自动机】

【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;

}

猜你喜欢

转载自blog.csdn.net/weixin_40317006/article/details/81386979