问题描述
为了简化该问题,我们约定,每个用户的 DN 是一个正整数,且不会重复。有若干种用户的属性,用正整数编号。每个用户可以具有这些属性中的若干个,且每个属性只能有一个值。每个属性的值也是一个正整数。例如,假定有两个用户:用户 1 和用户 2,他们的 DN 分别是 1 和 2。一共有 3 种属性。用户 1 具有属性 1 和属性 2,且属性 1 的值为 2,属性 2 的值为 3;但不具有属性 3。用户 2 具有属性 2 和属性 3,且属性 2 的值为 3,属性 3 的值为 1;但不具有属性 1。如下表所示:
DN | 属性 1 | 属性 2 | 属性 3 |
---|---|---|---|
1 | 2 | 3 | N/A |
2 | N/A | 3 | 1 |
一个匹配表达式可以是一个属性的值,也可以是多个匹配表达式的逻辑组合。只匹配一个属性的值的表达式称为原子表达式,原子表达式的形式为 <属性编号><操作符><属性值>
。其中操作符有两种:断言与反断言。断言操作符为 :
,表示匹配具有该属性且值与之相等的用户;反断言操作符为 ~
,表示匹配具有该属性且值与之不等的用户。例如,表达式 1:2
可以与上述用户 1 相匹配,但不能与用户 2 相匹配;而表达式 3~1
则不能与任何一个用户相匹配。
表达式可以进行逻辑组合,其语法是:<操作符>(表达式 1)(表达式 2)
。其中操作符有两种:与(&
)和或(|
)。如果操作符为与,则当且仅当两个表达式都与某一用户相匹配时,该表达式与该用户相匹配;如果操作符为或,则当且仅当两个表达式中至少有一个与某一用户相匹配时,该表达式与该用户相匹配。例如,表达式 &(1:2)(2:3)
可以与用户 1 相匹配,但不能与用户 2 相匹配;而表达式 |(1:2)(3:1)
则可以与两个用户都相匹配。
形式化地,上述语法用 BNF 范式表示如下:
NON_ZERO_DIGIT = "1" / "2" / "3" / "4" /
"5" / "6" / "7" / "8" / "9"
DIGIT = "0" / NON_ZERO_DIGIT
NUMBER = NON_ZERO_DIGIT / (NON_ZERO_DIGIT DIGIT*)
ATTRIBUTE = NUMBER
VALUE = NUMBER
OPERATOR = ":" / "~"
BASE_EXPR = ATTRIBUTE OPERATOR VALUE
LOGIC = "&" / "|"
EXPR = BASE_EXPR / (LOGIC "(" EXPR ")" "(" EXPR ")")
EASY_EXPR = BASE_EXPR /
(LOGIC "(" BASE_EXPR ")" "(" BASE_EXPR ")")
输入格式
从标准输入读入数据。
输入的第一行包含一个正整数 𝑛,表示用户的数目。
接下来 𝑛 行,每行包含空格分隔的若干个正整数,第一个正整数表示该用户的 DN,第二个正整数表示该用户具有的属性个数,此后的每两个正整数表示该用户具有的一个属性及其值。这些属性按照属性编号从小到大的顺序给出。
接下来一行包含一个正整数 𝑚,表示匹配表达式的数目。
接下来 𝑚 行,每行包含一个匹配表达式。
输出格式
输出到标准输出。
输出 𝑚 行,每行包含零个或多个正整数,用空格分隔,表示与对应的匹配表达式相匹配的用户的 DN,由小到大排序。
样例输入1
2
1 2 1 2 2 3
2 2 2 3 3 1
4
1:2
3~1
&(1:2)(2:3)
|(1:2)(3:1)
样例输出1
1
1
1 2
直接上代码和结果
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class DN
{
public:
vector<int> digit;
int num;
};
string::iterator search(string &a)
{
string::iterator p = a.begin() + 1;
int count = 1;
while (count != 0)
{
p++;
if (*p == '(')
count++;
else if (*p == ')')
count--;
}
return p;
}
bool check(string op,DN object)
{
if (op[0]>47&&op[0]<58)
{
if (op[1] == ':')
{
if (object.digit[op[0] - '0' - 1] == op[2] - '0')
{
return true;
}
else return false;
}
if (op[1] == '~')
{
if (object.digit.size() < op[1] - '0')
{
return false;
}
else if (object.digit[op[0] - '0' - 1] != op[2] - '0')
{
return true;
}
else return false;
}
}
else
{
string::iterator pt = search(op);
string op1(op.begin()+2, pt);
string op2(pt + 2, op.end() -1);
if (op[0] == '&')
{
return check(op1,object) && check(op2,object);
}
else if (op[0] == '|')
{
return check(op1, object) || check(op2, object);
}
}
}
int main()
{
vector<DN> list;
vector<string> op;
int m, n;
cin >> n;
for (int i = 0; i < n; i++)
{
DN input;
int num;
cin >> num;
input.num = num;
int t;
cin >> t;
for (int p = 0; p < t; p++)
{
int a, b;
cin >> a >> b;
int temp;
temp =a;
input.digit.resize(a);
input.digit[a - 1] = b;
}
list.push_back(input);
}
cin >> m;
for (int x = 0; x < m; x++)
{
string temp;
cin >> temp;
op.push_back(temp);
}
for (int i = 0; i < op.size(); i++)
{
for (int t = 0; t < list.size(); t++)
{
if (check(op[i], list[t]))
{
cout << list[t].num<<" ";
}
}
cout <<endl;
}
return 0;
}
空间占用达到了1.454个G,同时得分为0,我自己试了一下测试例是能通过的
我处理匹配表达式的思路是:将表达式的两个子表达式分开,具体的方法是找到与第一个(左括号匹配的)右括号,里面的就是第一个子表达式,往后两位的就是第二个子表达式,然后递归将这两个表达式分到不能再分,也就是原子表达式的形式,最后判断用户是否符合这个表达式来输出
求助各位大佬这个代码的问题在哪里,以及为什么会占用这么多空间,是因为递归没写对吗
是不是应该按照属性的类别来存储而不是用户的标签,这样匹配的时候可以直接用类别的地址来访问就不用顺序查找了