业务场景
用户需要设定规则,我们根据用户输入的数值判断是否符合规则。
示例一:
用户认为满足如下条件的属于合格品:
(width<62.15|height>9.278)&(area>5.86|width>6)
(数字我随便敲的)
现在来了一批样品,得到了属性width等的数值。要判断样品是否合规
拆解
这个问题本质上是判断这样的一串逻辑表达式
(63.2<62.15|553.14>37.278)&(1035.68>536.86|63.2>43)
不带括号
我们先从简单的入手:
没有括号的情况
表达式就剩下 < > | &运算符
其中< >优先级更高一些
仿照[LeetCode] Basic Calculator I II III 总结
给出如下解法:
#include<deque>
QString getNumStr(QString judgeValue,int i)
{
//浮点数
QString pattern("\\d+(\\.\\d+)");
QRegExp rx(pattern);
int pos = rx.indexIn(judgeValue, i);
if (pos > -1)
{
QString value = rx.cap(0);
QString temp = rx.cap(1);
return value;
}
//整数
pattern = "-?\\d+";
rx.setPattern(pattern);
pos = rx.indexIn(judgeValue, i);
if (pos > -1)
{
QString value = rx.cap(1);
return value;
}
return "";
}
bool cal(QString judgeValue)
{
judgeValue.remove(QRegExp("\\s"));
int i = 0;
double temp, curNum = 1;
QChar preSign; //之前的符号
std::deque<double> numberStack;
std::deque<QChar> signStack;
while (i < judgeValue.size() + 1)
{
QChar cur = judgeValue.at(i);
if (cur.isDigit())
{
QString value = getNumStr(judgeValue, i);
if (!value.isEmpty())
{
i = i + value.size() - 1;
curNum = value.toDouble();
}
i++;
continue;
}
else //遇到符号了,判断之前的符号
{
switch (preSign.unicode())
{
case '>':
{
temp = numberStack.back();
temp = temp > curNum;
numberStack.pop_back();
numberStack.push_back(temp);
break;
}
case '<':
{
temp = numberStack.back();
temp = temp < curNum;
numberStack.pop_back();
numberStack.push_back(temp);
break;
}
case '&':
{
numberStack.push_back(curNum);
signStack.push_back('&');
break;
}
case '|':
{
numberStack.push_back(curNum);
signStack.push_back('|');
break;
}
case ' ':
break;
default:
numberStack.push_back(curNum);
}
preSign = cur;
}
i++;
}
//
if (!numberStack.empty())
{
double result = numberStack.front();
numberStack.pop_front();
while (!numberStack.empty())
{
double cur = numberStack.front();
numberStack.pop_front();
QChar sign = signStack.front();
signStack.pop_front();
if (sign == "|")
{
result = cur || result;
}
else
{
result = cur && result;
}
}
return result;
}
else
{
return false;
}
}
int main(int argc, char *argv[])
{
cal(" 90.3< 62.15 | 36.12>9.278 & 69.214>5.86");
return 0;
}
技巧总结:
- 计算表达式 63.2<62.15: 需要存储 第一个数字63.2,运算符,第二个数字。遍历字符串时,指针走到62.15,其实不好判断是第几个数字。所以这里的处理办法是:指针走到下一个运算符,即 63.2<62.15| 中的 ‘|’,第一个数字存储在栈里,第二个存储在当前数字 curNum 中,前运算符保存在preSign中。这是才计算表达式63.2<62.15
- 与leetcode题不同的是,加减法可以被转换成正负号,我这里不行,所以需要先把所有的 <>算出,表达式里只剩下 数字和 | &之后,从队列numberStack,signStack取出数字和符号,从左到右计算逻辑运算符
带括号
两者的区别在于,需要先把括号里面的内容算出来,并返回一个结果
思路
所以:
bool cal(QString judgeValue)
{
judgeValue.remove(QRegExp("\\s"));
int i = 0;
double temp, curNum = 1;
QChar preSign; //之前的符号
std::deque<double> numberStack;
std::deque<QChar> signStack;
while (i < judgeValue.size() + 1)
{
QChar cur = judgeValue.at(i);
if (cur.isDigit())
{
QString value = getNumStr(judgeValue, i);
if (!value.isEmpty())
{
i = i + value.size() - 1;
curNum = value.toDouble();
}
i++;
continue;
}
else if(cur == '(')
{
int j = i, count = 0;
for (; i < judgeValue.size(); i++) {
if (judgeValue[i] == '(') count++;
if (judgeValue[i] == ')') count--;
if (count == 0) break;
}
curNum = cal(judgeValue.mid(j + 1, i - j - 1));
}
else //遇到逻辑符号了,判断之前的符号
{
switch (preSign.unicode())
{
case '>':
{
temp = numberStack.back();
temp = temp > curNum;
numberStack.pop_back();
numberStack.push_back(temp);
break;
}
case '<':
{
temp = numberStack.back();
temp = temp < curNum;
numberStack.pop_back();
numberStack.push_back(temp);
break;
}
case '&':
{
numberStack.push_back(curNum);
signStack.push_back('&');
break;
}
case '|':
{
numberStack.push_back(curNum);
signStack.push_back('|');
break;
}
case ' ':
break;
default:
numberStack.push_back(curNum);
}
preSign = cur;
}
i++;
}
//
if (!numberStack.empty())
{
double result = numberStack.front();
numberStack.pop_front();
while (!numberStack.empty())
{
double cur = numberStack.front();
numberStack.pop_front();
QChar sign = signStack.front();
signStack.pop_front();
if (sign == "|")
{
result = cur || result;
}
else
{
result = cur && result;
}
}
return result;
}
else
{
return false;
}
}
解法
总的解法就是:
- 客户输入(width > 100 & height > 300) | (area < 600)
- 获取属性值 {“width” : 321, “height” : 560 , “area”: 235}
- 利用字符串替换得到要计算的表达式: (321> 100 & 560 > 300) | (235< 600)
- 计算bool cal(QString judgeValue); 返回结果
搞定