逆波兰表达式:解锁关键词文本匹配的高效密码
在文本处理场景中,我们经常需要根据规则判断文本是否满足特定条件。本文通过一个实际案例,讲解如何利用逆波兰表达式解决复杂的关键词匹配问题。
一、业务场景分析
输入文本:你们可以帮我调查一下
匹配规则:帮我&(调查|处理)
规则说明:文本需同时满足以下两个条件
- 包含关键词
帮我 - 包含
调查或处理中的任意一个
若文本满足规则,则返回命中,否则返回未命中。
二、逆波兰表达式简介
逆波兰表达式(后缀表达式) 是一种数学表达式表示方法,其特点是将运算符放在操作数之后。例如,中缀表达式 A+B 对应的逆波兰表达式为 A B +。这种表示法无需括号即可明确运算顺序,适合用栈结构进行计算。
三、实现思路
-
规则解析
将规则字符串分解为操作数和运算符。例如,帮我&(调查|处理)解析为:["帮我", "&", "(", "调查", "|", "处理", ")"]。 -
中缀转后缀表达式
利用栈结构处理运算符优先级,将中缀表达式转换为逆波兰表达式。例如,帮我&(调查|处理)转换为帮我 调查 处理 | &。 -
后缀表达式求值
遍历逆波兰表达式,使用栈结构进行布尔运算,最终得到匹配结果。
四、代码实现
1. 规则分词
将规则字符串拆分为操作数和运算符列表。
public static List<String> tokenize(String rule) {
List<String> tokens = new ArrayList<>();
StringBuilder currentWord = new StringBuilder();
for (char c : rule.toCharArray()) {
if (c == '&' || c == '|' || c == '(' || c == ')') {
if (currentWord.length() > 0) {
tokens.add(currentWord.toString());
currentWord.setLength(0);
}
tokens.add(String.valueOf(c));
} else {
currentWord.append(c);
}
}
if (currentWord.length() > 0) {
tokens.add(currentWord.toString());
}
return tokens;
}
2. 中缀表达式转后缀表达式
处理运算符优先级和括号。
public static List<String> infixToPostfix(List<String> infixTokens) {
List<String> output = new ArrayList<>();
Deque<String> stack = new LinkedList<>();
Map<String, Integer> precedence = new HashMap<>();
precedence.put("&", 2);
precedence.put("|", 1);
precedence.put("(", 0);
for (String token : infixTokens) {
if (isOperator(token) || token.equals("(") || token.equals(")")) {
if (token.equals("(")) {
stack.push(token);
} else if (token.equals(")")) {
while (!stack.isEmpty() && !stack.peek().equals("(")) {
output.add(stack.pop());
}
stack.pop(); // 弹出左括号
} else { // 处理运算符
while (!stack.isEmpty() && precedence.get(stack.peek()) >= precedence.get(token)) {
output.add(stack.pop());
}
stack.push(token);
}
} else { // 处理关键词
output.add(token);
}
}
while (!stack.isEmpty()) {
output.add(stack.pop());
}
return output;
}
private static boolean isOperator(String token) {
return token.equals("&") || token.equals("|");
}
3. 后缀表达式求值
根据文本内容计算布尔结果。
public static boolean evaluatePostfix(List<String> postfixTokens, String text) {
Deque<Boolean> stack = new LinkedList<>();
for (String token : postfixTokens) {
if (isOperator(token)) {
boolean b = stack.pop();
boolean a = stack.pop();
if (token.equals("&")) {
stack.push(a && b);
} else if (token.equals("|")) {
stack.push(a || b);
}
} else {
stack.push(text.contains(token));
}
}
return stack.pop();
}
4. 主函数测试
结合业务场景验证结果。
public static void main(String[] args) {
String text = "你们可以帮我调查一下";
String rule = "帮我&(调查|处理)";
List<String> infixTokens = tokenize(rule);
List<String> postfixTokens = infixToPostfix(infixTokens);
boolean result = evaluatePostfix(postfixTokens, text);
System.out.println("文本是否命中规则:" + result); // 输出:true
}
五、总结
通过逆波兰表达式,我们能够优雅地处理复杂的逻辑匹配规则。其核心优势在于:
- 消除括号带来的优先级歧义。
- 通过栈结构实现高效计算。
本文代码可直接应用于关键词过滤、规则引擎等场景。