HihoCoder 1289 403 Forbidden 字典树

烂漫一生 提交于 2020-01-22 01:29:12

题目链接(vj):https://cn.vjudge.net/problem/HihoCoder-1289

题目

  • 题目大意:n条ip/mask.输入规则为allow | deny address或者allow | deny address/mask,比如allow 1.2.3.4/30(allow可以访问),deny 1.1.1.1(deny不可以访问)。再输入一个ip进行访问要求,有几种对应方式:
  1. 和无mask的ip相同,那么能否访问取决于那个op是allow还是deny
  2. 和有mask的ip对应,这种方式是指ip的32位的2进制的表示方式的前mask位一样,那么就算对应。
10000000 01111111 0000,0100 01100100 (128.127.4.100)
10000000 01111111 0000,1000 01111101 (128.127.8.125)
当mask = 20,因为前20位相同,所以可以匹配

如果找不到对应的ip,那么就是可以访问。

思路

  • 因为第一次做字典树,所以大部分看的别人代码。模板字典树用数组存储(参考qy模板)。
struct Trie
{
    int cnt, root, go[maxL][2], id[maxL], tag[maxL];

    int create()
    {
        ++cnt;
        go[cnt][0] = go[cnt][1] = -1;
        return cnt;
    }

    void init()
    {
    	//memset(tag, -1, sizeof(tag));
    	memset(id, 0, sizeof(id));
        cnt = 0;
        root = create();
    }
}
  • 按照ip的二进制形式来创建字典树。将输入的ip4个数转换成llong的一个数,然后把这个数每一位是0还是1存入字典树。
scanf("%s %d.%d.%d.%d", op, &a1, &a2, &a3, &a4);
llong b = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4;
T.insert(b, mask, i);
  • 插入结点时判断其是否有mask,如果是就是要插入32位数,如果有mask,就只需要插入这个数的前mask位数。
  • 寻找结点时注意先要对根节点进行判断,理由是当有mask等于0时其实所有的ip都可以对应。
  • 题目有这么一句:the rules are checked in sequence until the first match is found,这句话表示我们去找对应的结点时应该是输入顺序最早的那一个,所以当我们在字典树搜到一个答案时不能立刻返回,而是继续去找那个插入顺序最前的那个答案,所以我们需要一个id数组来记录这个树的这个分支是第几个插入的(其实就是数组输入顺序)。

代码

因为需要代码带解释,这次就不放链接直接放代码。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

typedef long long llong;
const int INF = 0x3f3f3f3f;
const int maxL = 4000000;
char op[10];

struct Trie
{
    int cnt, root, go[maxL][2], id[maxL], tag[maxL];    //id记录输入顺序,tag记录这个是allow还是deny

    int create()
    {
        ++cnt;
        go[cnt][0] = go[cnt][1] = -1;
        return cnt;
    }

    void init()
    {
    	//memset(tag, -1, sizeof(tag));
    	memset(id, 0, sizeof(id));
        cnt = 0;
        root = create();
    }

    void insert(llong x, int mask, int num)
    {
    	int tmp = 0;
    	if (mask > -1)
    		tmp = 32 - mask;

    	int p = root;
    	for (int i=31; i>=tmp; i--)
    	{
    		llong y = x;
    		int bin = (y >> i) & 1;
    		if (go[p][bin] == -1)
    			go[p][bin] = create();
    		p = go[p][bin];
    	}

    	if (!id[p])             //如果这个是之前没有出现过的
    	{
    		id[p] = num;
    		if (op[0] == 'a')
    			tag[p] = 1;
    		else
    			{
    			    tag[p] = 0;
    			    //printf("tag[%d]=%d\n", p, tag[p]);
    			}
    	}
    }

    bool search(llong x)
    {
    	bool ans = true;	//没找到就是yes
    	int p = root, id_min = INF;

    	if (id[p])			//先对根节点进行讨论,因为有mask = 0的情况
    	{
    		id_min = id[p];
    		ans = tag[p];
    	}

    	for (int i=31; i>=0; i--)
    	{
    		llong y = x;

    		int bin = (y >> i) & 1;
            //printf("go[%d][%d] = %d\n", p, bin, go[p][bin]);
    		if (go[p][bin] == -1)
    			return ans;
    		p = go[p][bin];

    		if (id[p] && id[p] < id_min)    //去找id最小的那个
    		{
    			id_min = id[p];
    			ans = tag[p];
    		}
    	}
    	return ans;
    }

};

Trie T;


int main()
{
	T.init();
	int n, m;
	while (scanf("%d %d", &n, &m) != EOF)
	{
		int a1, a2, a3, a4, mask;

		for (int i=1; i<=n; i++)
		{
			mask = -1;
			scanf("%s %d.%d.%d.%d", op, &a1, &a2, &a3, &a4);
			char c = getchar();

			if (c == '/')
			{
				scanf("%d", &mask);
			}
			llong b = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4;
			T.insert(b, mask, i);
		}

		while (m--)
		{
			scanf("%d.%d.%d.%d", &a1, &a2, &a3, &a4);
			llong b = (a1 << 24) + (a2 << 16) + (a3 << 8) + a4;

			if (T.search(b))
				puts("YES");
			else
				puts("NO");
		}
	}
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!