Little Hi runs a web server. Sometimes he has to deny access from a certain set of malicious IP addresses while his friends are still allow to access his server. To do this he writes N rules in the configuration file which look like:
allow 1.2.3.4/30 deny 1.1.1.1 allow 127.0.0.1 allow 123.234.12.23/3 deny 0.0.0.0/0
Each rule is in the form: allow | deny address or allow | deny address/mask.
When there comes a request, the rules are checked in sequence until the first match is found. If no rule is matched the request will be allowed. Rule and request are matched if the request address is the same as the rule address or they share the same first mask digits when both written as 32bit binary number.
For example IP "1.2.3.4" matches rule "allow 1.2.3.4" because the addresses are the same. And IP "128.127.8.125" matches rule "deny 128.127.4.100/20" because 10000000011111110000010001100100 (128.127.4.100 as binary number) shares the first 20 (mask) digits with10000000011111110000100001111101 (128.127.8.125 as binary number).
Now comes M access requests. Given their IP addresses, your task is to find out which ones are allowed and which ones are denied.
Line 1: two integers N and M.
Line 2-N+1: one rule on each line.
Line N+2-N+M+1: one IP address on each line.
All addresses are IPv4 addresses(0.0.0.0 - 255.255.255.255). 0 <= mask <= 32.
For 40% of the data: 1 <= N, M <= 1000.
For 100% of the data: 1 <= N, M <= 100000.
For each request output "YES" or "NO" according to whether it is allowed.
5 5 allow 1.2.3.4/30 deny 1.1.1.1 allow 127.0.0.1 allow 123.234.12.23/3 deny 0.0.0.0/0 1.2.3.4 1.2.3.5 1.1.1.1 100.100.100.100 219.142.53.100
YES YES NO YES NO
本题非常适合我这样的菜鸟:读完题目很容易有一个基本的思路,换句话说,这可以实现这个功能,但不保证性能。
由题意直观的可以想到以下做法:
仔细想想Naive Approach的问题,对于一个IP,每次检查的时候复杂度为O(n),特别地,当这个IP没有任合匹配规则时,要尝试完所有的规则。 有什么办法可以准确的知道一个IP适用哪些规则呢?当然,所谓“适用”其实,题目说明的很清楚,即IP的mask
长的前缀必须匹配。
前缀匹配问题,可以用前缀树来解决。因此,可以利用前缀树来确定哪些规则可以适用于一个IP。
于是有了下面的想法:
至此,剩下的问题就是如果建立前缀树,由于题意要求,最早匹配原则,树的建立可以这样做:
由此,当检查一个IP时,只需要返回这个IP适用的最长规则(为什么?上面的性质可以想一想)
Solution:有了上面的规则,代码就比较简单了。
下面是完整的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using
namespace
std;
//trie节点定义
class
trieNode {
public
:
trieNode() : order(0) {
for
(
int
i = 0; i < 2; ++i)
children[i] = NULL;
}
~trieNode() {
for
(
int
i = 0; i < 2; ++i) {
if
(children[i] != NULL) {
delete
children[i];
children[i] = NULL;
}
}
}
public
:
int
order;
//1 is allow,-1 is deny
trieNode *children[2];
//该数组记录指向孩子的指针
};
//trie树定义
class
trie {
public
:
trie() : root(
new
trieNode) {}
void
insert(unsigned
int
ip,
int
mask,
int
order);
//插入ip
bool
find(unsigned
int
ip);
//查找ip
public
:
trieNode *root;
};
void
trie::insert(unsigned
int
ip,
int
mask,
int
order) {
trieNode *cur = root;
for
(
int
i = 0; i < mask; ++i) {
if
(cur->order)
return
;
int
bit = (ip >> (31 - i)) & 0x01;
if
(!cur->children[bit]) {
cur->children[bit] =
new
trieNode;
}
cur = cur->children[bit];
}
if
(!cur->order)
//若原先已经有该条规则,则不覆盖(就先原则)
cur->order = order;
//设置属性:allow 或 deny
}
bool
trie::find(unsigned
int
ip) {
trieNode *cur = root;
int
order = 1;
for
(
int
i = 0; i < 32; ++i) {
int
bit = (ip >> (31 - i)) & 0x01;
if
(cur->order) {
order = cur->order;
}
if
(!cur->children[bit]) {
break
;
}
cur = cur->children[bit];
}
return
order > 0 ?
true
:
false
;
}
int
main()
{
int
N, M;
cin >> N >> M;
trie rule;
//define a trie
for
(
int
i = 1; i <= N; ++i) {
string cmd;
//allow or deny
cin >> cmd;
char
ch;
unsigned
int
a, b, c, d, ip;
cin >> a >> ch >> b >> ch >> c >> ch >> d;
ip = (a << 24) | (b << 16) | (c << 8) | d;
int
mask = 32;
cin >> ch;
if
(ch ==
'/'
)
cin >> mask;
else
cin.putback(ch);
//将刚才读取的ch字符返回到输入流中
int
order = 1;
if
(cmd !=
"allow"
)
order = -1;
rule.insert(ip, mask, order);
}
for
(
int
i = 0; i < M; ++i) {
unsigned
int
a, b, c, d, ip;
char
ch;
cin >> a >> ch >> b >> ch >> c >> ch >> d;
ip = (a << 24) | (b << 16) | (c << 8) | d;
if
(rule.find(ip))
cout <<
"YES"
<< endl;
else
cout <<
"NO"
<< endl;
}
system
(
"pause"
);
return
0;
}
|