题目链接(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进行访问要求,有几种对应方式:
- 和无mask的ip相同,那么能否访问取决于那个op是allow还是deny
- 和有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;
}
来源:CSDN
作者:王大凤
链接:https://blog.csdn.net/qq_35414878/article/details/104063579