论scanf和cin的效率差别
unsigned int getIP(void)
{
//char t;
unsigned int a, b, c, d;
//cin >> a >> t >> b >> t >> c >> t >> d;
scanf("%d.%d.%d.%d", &a, &b, &c, &d);
return((a << 24) | (b << 16) | (c << 8) | d);
}
定义结构体、结构体初始化、结构体变量的声明:
struct trie{
int prior;
trie* child[2];
trie() { prior = 0; memset(child, 0, sizeof(child)); }
};
trie* root;
结构体变量初始化:
root = new trie();
这一句必须放在main()里,放在外面会报错:“此声明没有存储类或类型说明符”
完整代码:
/* 此题有O(m*n)的暴力解法,运行超时。
* 使用前缀树,使查找一条记录的时间从O(n)变为O(1),总共O(min(n,m))。
*/
#include <iostream>
//hiho上面使用memset必须引用<cstring>
#include <cstring>
//VS2017只用<cstring>的话没法 cin >> string
#include <string>
using namespace std;
//前缀树
struct trie{
int command;
trie* child[2];//32位二进制ip地址,只有0或1的分支。
trie() { command = 0; memset(child, 0, sizeof(child)); }
};
trie* root;
//注意root = new trie();语句不能放在这里。
//将输入的IPv4格式(128.127.8.125)转换成一个整数
unsigned int getIP(void)
{
char t;
unsigned int a, b, c, d;
//利用char巧妙分开几个int,避免了使用string读入一整行再另作处理的麻烦。
cin >> a >> t >> b >> t >> c >> t >> d;
//这里的 移位 和 取或代替加法 很巧妙。
return((a << 24) | (b << 16) | (c << 8) | d);
}
/*
* 将当前的一条规则加入到规则前缀树中
* 因为检索时按顺序访问规则,所以后插入的规则始终不可覆盖已存在的规则。
* 即,若规则1的终点在规则2的必经之路上,那么规则2没有存在的必要。
* 因此,插入规则时,若途经(包括本规则的终点)已被标记为终点的节点,则应抛弃本规则。
* 检索时,应寻找符合的最长路径,因为这必是该记录第一条匹配的规则。
*/
/*
* 也可以不管上述规则,插入规则时只要终点不与之前的规则重合,就插入。
* 但需要在节点中记录该规则是第几条,检索时需要一路记录遇到的编号最小的终点。
*/
void addIP(unsigned int ip, int mask, int command)
{
trie* rule = root;
int bit;
for (int j = 0; j < mask; j++)
{
//如果当前节点已经是某规则的终点
if (rule->command) return;
//从二进制最高位(记作第0位)开始,取第j位
bit = ((ip >> (31 - j)) & 1);
//如果下一个节点没被访问过,则分配空间
if (!(rule->child)[bit])
{
(rule->child)[bit] = new trie();
}
//下面这句必须放在上面的if循环外。因为不管下一个节点是否已被访问过,当前的路径都需要继续访问。
rule = (rule->child)[bit];
}
//下面的if条件不能省略。否则如果有先后两条相同ip的规则,后面的会覆盖前面的。
if (!rule->command)
rule->command = command;
return;
}
//检索某条IP记录
void checkIP(unsigned int ip)
{
int bit;
trie* rule = root;
//command初始值设为1,这样如果没有匹配的规则,会输出"YES"。
int command = 1;
//需要走完当前IP的全部32位二进制,因为要找出最长的匹配路径。只有没路了才停下。
for (int j = 0; j < 32; j++)
{
bit = ((ip >> (31 - j)) & 1);
//随时记录遇到的最后一个终点
if (rule->command)
{
command = rule->command;
}
//如果本记录走不下去了,即没有更早出现的匹配记录了
if (!(rule->child)[bit]) break;
//还能走,所以继续走
rule = (rule->child)[bit];
}
//按规则给出allow或deny的指令
cout << ((command > 0) ? "YES" : "NO") << endl;
return;
}
int main()
{
int n, m;
string cmd;
unsigned int ip;
char t;
int mask;
root = new trie();
cin >> n >> m;
//建立前缀二叉树
for (int i = 0; i < n; i++)
{
mask = 32;
cin >> cmd;
ip = getIP();
cin >> t;
if (t == '/')cin >> mask;
//cin.putback(char)的用法
else cin.putback(t);
int command = (cmd[0] == 'a') ? 1 : (-1 );
addIP(ip, mask, command);
}
//按规则检索IP地址,并输出结果
for (int i = 0; i < m; i++)
{
ip = getIP();
checkIP(ip);
}
return 0;
}