HDU 4760 Good FireWall 完善Trie题解

本题乍看像是线段树之类的区间操作,不过因为只是需要查找ip的前缀,故此其实是使用Trie来做。

这里的Trie使用到了Delete函数,这是个Trie函数中最难的函数了,当然要使用数组记录的方法水掉,也是可以的。这里不水,给出delete函数。

考点难点:

1 Trie的操作函数的灵活运用,主要难点是delete函数的灵活运用

2  在叶子节点所有的group id, 删除的时候要注意,不能一气删除了,有多个group id会挂在同一颗树中

3  源ip和目的ip也许在多个叶子节点中,要使用两个vector记录所有group id,然后使用查找两集合是否有公共值的算法解之

4 ip值转换为一个整数值记录就可以了,这里使用了unsigned,不过应该是int也可以的,因为只需要操作其二进制值,和整数值无关。

5 使用mask值,mask值后面的0可以不管,加速程序

所有问题考虑全,高级数据结构中包括各种小问题处理,大算法中使用到多种小算法,综合难度超过5星级。做完后很有成就感O(∩_∩)O哈哈~。

#include <stdio.h>
#include <bitset>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX_ID = 1024;
const int MAX_LINE = 32769;
const int MAX_N = 15;
const int ARR_SIZE = 2;
const int MAX_DIGIT = 32;
const int MAX_NODE = MAX_ID*MAX_DIGIT*MAX_N;
unsigned gEnableIdIpMask[MAX_ID][MAX_N<<1];
int gLen[MAX_ID], id;

struct Node
{
	int n;
	vector<int> id;
	Node *arr[ARR_SIZE];
};

void clearNode(Node *rt)
{
	rt->n = 0;
	rt->id.clear();
	for (int i = 0; i < ARR_SIZE; i++)
	{
		rt->arr[i] = NULL;
	}
}

Node pool[MAX_NODE];
int poolID;

void insertTrie(Node *trie, unsigned dig, unsigned mark, int id)
{
	bitset<MAX_DIGIT> bi = dig;
	int m = MAX_DIGIT - mark;
	for (int i = MAX_DIGIT-1; i >= m; i--)
	{
		Node *&p = trie->arr[bi[i]];//注意*&p,操作同名地址变量
		if (!p)//判断成if(p),程序崩溃。
		{
			p = &pool[poolID++];
			clearNode(p);
		}
		trie = p;
	}
	trie->n++;
	trie->id.push_back(id);
}

bool searchNode(Node *trie, unsigned dig, vector<int> &id)
{
	bitset<MAX_DIGIT> bi = dig;
	bool flag = false;
	for (int i = MAX_DIGIT-1; i >= 0; i--)
	{
		trie = trie->arr[bi[i]];
		if (!trie) return flag;
		if (trie->n)
		{
			flag = true;
			id.insert(id.end(), trie->id.begin(), trie->id.end());
		}
	}
	return flag;
}

inline bool isFreeNode(Node *rt)
{
	for (int i = 0; i < ARR_SIZE; i++)
	{
		if (rt->arr[i]) return false;
	}
	return true;
}

bool deleteNodeHelper(Node *trie, bitset<MAX_DIGIT> &bi, int mask, int id,
				  int lv = MAX_DIGIT-1)//注意lv含义,准确赋值
{
	if (trie)
	{
		if (mask-1 == lv)//注意:下标计算,对齐
		{
			if (trie->n)
			{
				if (trie->n > 1)
				{
					int j = 0;
					for (; j < trie->n; j++) if (trie->id[j] == id) break;
					trie->id.erase(trie->id.begin()+j);
					trie->n--;
				}
				else
				{
					trie->n = 0;
					trie->id.clear();
					return isFreeNode(trie);
				}
			}
		}
		else
		{
			if (deleteNodeHelper(trie->arr[bi[lv]], bi, mask, id, lv-1))
			{
				trie->arr[bi[lv]] = NULL;
				return !trie->n && isFreeNode(trie);
			}
		}
	}
	return false;
}

inline void deleteNode(Node *trie, unsigned dig, unsigned mask, int id)
{
	bitset<MAX_DIGIT> bi = dig;
	int m = MAX_DIGIT - mask;	//注意长度计算
	deleteNodeHelper(trie, bi, m, id);
}

unsigned getIP()
{
	unsigned ip = 0;
	unsigned part;
	for (int j = 3; j >= 0; j--)
	{
		scanf("%u", &part);
		getchar();
		ip |= part<<(j<<3);
	}
	return ip;
}

int main()
{
	char cmd;
	unsigned ip, mask;
	Node *trie = &pool[0];
	clearNode(trie);
	poolID = 1;
	while (scanf("%c", &cmd) != EOF)
	{
		if (cmd == 'E')
		{
			scanf("%d", &id);
			scanf("%d", gLen+id);
			for (int i = 0; i < gLen[id]; i++)
			{
				ip = getIP();
				scanf("%u", &mask);
				gEnableIdIpMask[id][i<<1] = ip;
				gEnableIdIpMask[id][i<<1|1] = mask;
				insertTrie(trie, ip, mask, id);
			}
			getchar();
		}
		else if (cmd == 'D')
		{
			scanf("%d", &id);
			getchar();
			for (int i = 0; i < gLen[id]; i++)
			{
				deleteNode(trie, gEnableIdIpMask[id][i<<1],
					gEnableIdIpMask[id][i<<1|1], id);
			}
			gLen[id] = 0;
		}
		else// if (cmd == 'F')
		{
			ip = getIP();
			vector<int> ipSrc, ipDes;
			bool src = searchNode(trie, ip, ipSrc);
			ip = getIP();
			if (src) src = searchNode(trie, ip, ipDes);
			if (src)
			{
				sort(ipSrc.begin(), ipSrc.end());
				sort(ipDes.begin(), ipDes.end());
				bool hasSame = false;
				for (int i = 0, j = 0;
					i < (int)ipSrc.size() && j < (int)ipDes.size(); )
				{
					if (ipSrc[i] == ipDes[j])
					{
						hasSame = true;
						break;
					}
					else if (ipSrc[i] < ipDes[j]) i++;
					else j++;
				}
				if (hasSame) puts("F");
				else puts("D");
			}
			else puts("D");
		}
	}
	return 0;
}

HDU 4760 Good FireWall 完善Trie题解

时间: 2024-10-06 20:38:23

HDU 4760 Good FireWall 完善Trie题解的相关文章

HDU 1247 Hat’s Words Trie题解

使用Trie的insert函数,主要是要灵活修改search函数,使得其可以快速搜索hat word. 思路: 1 先搜索一个word的前缀有没有在Trie树中找到,如果找到,保存这个Node,方便下面继续搜索, 就搜索余下的能不能是Trie树上的一个单词,如果找到,那么就是hat word了. 2 如果没找到,那么就沿着刚刚搜索到的前缀单词的节点继续往下搜,这样就可以加速程序,不用每次重复搜索. 3 如果沿着Trie树已经走到word的叶子节点了,那么就结束这个word的搜索了. 实现好这些思

hdu 4760 Good Firewall(字典树)

题目链接:hdu 4760 Good Firewall 题目大意:有一个防火墙,具有添加一个子网络,删除一个子网络,以及转发包的操作. 添加操作包含子网络的id,以及子网络的子网掩码(计算出网络前缀,以及ip的下限),不会超过15个. 删除则是给定要删除的子网络id. 转发操作,给定两个ip,如果两个ip在同一个子网络中,则可以转发,否则丢弃. 解题思路:对子网掩码前缀建立字典树,每个前缀终止节点用一个set记录属于哪些子网络,ip下限.那么增加和删除操 作既可以解决了.对于查询操作,分别查询两

HDU 1251 统计难题 Trie题解

基本上是标准的寻找前缀的问题,只需要insert和search函数就可以了. 我这里主要是修改一下n的记录方法,这里的n代表的不是叶子节点的标志,而是有多少单词经过了这条路径的标志. 然后是查找需要查找的前缀单词,如果没有找到,就返回0,表示没有单词以这个前缀单词为前缀,如果找到,直接返回n就是答案了.因为有n个单词经过了这条路径. 查找效率是常数. 使用静态分配空间的办法. #include <stdio.h> #include <string.h> const int MAX_

HDU 1075 What Are You Talking About Trie题解

翻译火星语,不过火星语也是使用英文单词的,就是把一个单词对应到另外一个单词. 可以使用map, 使用二分,方法很多. 不过最快的应该都是Trie解法了. 把火星语挂在Trie树中,然后在叶子节点增加一个string容器,装英语单词. 查找的时候,找到了出现在Trie中的火星语,就返回string就可以了. #include <stdio.h> #include <string> #include <string.h> using namespace std; const

HDU 2896 病毒侵袭 AC自动机题解

本题是在text里面查找key word的增强版,因为这里有多个text. 那么就不可以简单把Trie的叶子标志记录修改成-1进行加速了,可以使用其他技术,我直接使用个vis数组记录已经访问过的节点,达到加速效果,速度还算挺快的. 不过看discuss里面有人直接使用Trie,做出了140ms的速度,而且他的程序严格来说并不正确,可见本题的数据很水啊.Trie的时间效率肯定比AC自动机低,但是在数据很水的特殊情况下,Trie的速度也可以很快的. 注意两个细节: 1 病毒也需要安装顺序输出,不小心

hdu 6059 Kanade&#39;s trio(trie+容斥)

题目链接:hdu 6059 Kanade's trio 题意: 给你n个数,让你找有多少个(i,j,k),使得i<j<k满足a[i]^a[j]<a[j]^a[k]. 题解: 首先考虑a[i]和a[k],将他们都转换成二进制,对于a[i]和a[k],我们用Bi[p]表示二进制下的a[i]的第p位.考虑a[i]和a[k]二进制不同的最高位,这里假设为p,如果Bi[p]=0,Bk[p]=1,那么Bj[p]要为0,才能使得a[i]^a[j]<a[j]^a[k].(因为p前面的位相同,只有亦

HDU 1231 最大连续子序列 DP题解

典型的DP题目,增加一个额外要求,输出子序列的开始和结尾的数值. 增加一个记录方法,nothing special. 记录最终ans的时候,同时记录开始和结尾下标: 更新当前最大值sum的时候,更新开始节点. const int MAX_N = 10001; long long arr[MAX_N]; int N, sta, end; long long getMaxSubs() { long long sum = 0, ans = LLONG_MIN; int ts = 0; for (int

HDU 1160 FatMouse&#39;s Speed DP题解

本题就先排序老鼠的重量,然后查找老鼠的速度的最长递增子序列,不过因为需要按原来的标号输出,故此需要使用struct把三个信息打包起来. 查找最长递增子序列使用动态规划法,基本的一维动态规划法了. 记录路径:只需要记录后继标号,就可以逐个输出了. #include <stdio.h> #include <algorithm> using namespace std; const int MAX_N = 1005; struct MouseSpeed { int id, w, s; b

HDU 1385 Minimum Transport Cost 最短路径题解

本题就是使用Floyd算法求所有路径的最短路径,并且需要保存路径,而且更进一步需要按照字典顺序输出结果. 还是有一定难度的. Floyd有一种很巧妙的记录数据的方法,大多都是使用这个方法记录数据的. 不过其实本题数据不是很大,一般太大的数据也无法使用Floyd,因为效率是O(N^3). 所以其实也可以使用一般的Floyd算法,然后增加个三维数组记录数据.下面就是这种做法,0ms过了. #include <stdio.h> #include <vector> using std::v