华为OD 提取字符串中最长数学表达式(通过88.9%的测试用例) Java

题目描述

提取字符串中的最长合法简单数学表达式,字符串长度最长的,并计算表达式的值。如果没有,则返回0。简单数学表达式只能包含以下内容:0-9数字,符号 +-*

说明:

  1. 所有数字,计算结果都不超过long
  2. 如果有多个长度一样的,请返回第一个表达式的结果
  3. 数学表达式,必须是最长的,合法的
  4. 操作符不能连续出现,如 *-+1 是不合法的

输入

字符串

输出

表达式值

示例

输入:
1-2abcd

输出:
-1

Java代码(通过88.9%的测试用例)

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    private static final String v = "0123456789+-*";

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str = scanner.nextLine();
        List<String> formulas = new ArrayList<>();
        char[] chars = str.toCharArray();
        
        // 提取可能的合法表达式
        for (int i = 0; i < str.length(); i++) {
            char c = chars[i];
            if (Character.isDigit(c)) {
                int start = i;
                while (i + 1 < chars.length && v.contains(chars[i + 1] + "")) {
                    if (!Character.isDigit(chars[i]) && !Character.isDigit(chars[i + 1]) && chars[i + 1] != '-') {
                        break;
                    }
                    i++;
                }
                if (!Character.isDigit(str.charAt(i))) {
                    formulas.add(str.substring(start, i));
                } else {
                    formulas.add(str.substring(start, i + 1));
                }
            }
        }

		// 按长度排序,找到最长的表达式
        Collections.sort(formulas, (f1, f2) -> Integer.compare(f2.length(), f1.length()));
        if (formulas.size() > 0) {
            String res = calculateFormula(formulas.get(0));
            System.out.println(res);
        } else {
            System.out.println(0);
        }

    }

	/**
     * 计算给定表达式的值
     * @param formula 数学表达式字符串
     * @return 表达式的计算结果
     */
    private static String calculateFormula(String formula) {
        char[] chars = formula.toCharArray();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
            if (Character.isDigit(chars[i])) {
                builder.append(chars[i]);
            } else if (!Character.isDigit(chars[i]) && i != chars.length - 1) {
                builder.append(",").append(chars[i]).append(",");
            }
        }
        List<String> list = Stream.of(builder.toString().split(",")).collect(Collectors.toList());
        
        // 处理每个数字,以应对例如:"0200"的情况,将其转化为:"200"
        for (int i = 0; i < list.size(); i++) {
            if (!list.get(i).equals("+") && !list.get(i).equals("*") && !list.get(i).equals("-")) {
                long num = Long.parseLong(list.get(i));
                list.set(i, num + "");
            }
        }
        if (list.size() == 1) {
            return list.get(0);
        }

		// 先计算乘法,再计算加减
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("*")) {
                calculate(list, i, "*");
                i = 0;
            }
        }
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("+")) {
                calculate(list, i, "+");
                i = 0;
            }
            if (i < list.size() && list.get(i).equals("-")) {
                calculate(list, i, "-");
                i = 0;
            }
        }

        return list.get(0);
    }

    private static void calculate(List<String> list, int i, String op) {
        long a = Long.parseLong(list.get(i - 1));
        long b = Long.parseLong(list.get(i + 1));
        long res = 0L;
        switch (op) {
            case "*":
                res = a * b;
                break;
            case "+":
                res = a + b;
                break;
            case "-":
                res = a - b;
                break;
        }
        list.set(i, res + "");
        list.remove(i-1);
        list.remove(i);
    }
}

问题总结

1. 代码参考:题目0124-最长合法表达式

尽管参考,但建议各位不要全数抄写参考的代码,因为里面的问题还是不少的,需要注意判别,例如:

  1. 在判断表达式是否合法的条件上设置错误
    // 判断条件:操作符不能连续出现
    // 错误❌
    if (!Character.isDigit(cur) && !Character.isDigit(chars[i + 1])) {
    	break;
    }
    // 正确✔
    if (!Character.isDigit(chars[i]) && !Character.isDigit(chars[i + 1])) {
    	break;
    }
    
  2. 没有处理可能的表达式最后一个字符是否为操作符的情况

2. 为什么不使用正则表达式 regex = "[0-9+\\-*]+"

因为如果使用正则表达式regex,将输入的字符串 str 通过 str = str.replaceAll(regex, ""),会删去字符串中非0-9数字以及符号 ±*外的其他字符,但是这也会导致一些问题,例如:当我们输入:1+2+3abc45+1 时,如果采用正则表达式的方式,会导致获取的最长表达式为:1+2+345+1,但是这明显是不对的,而通过判断各个字符是否符合标准的方式则得到的最长表达式为:1+2+3

3. 处理可能字符串的最后一个字符

在代码中有如此一段:

if (!Character.isDigit(str.charAt(i))) {
	formulas.add(str.substring(start, i));
} else {
	formulas.add(str.substring(start, i + 1));
}

这是因为在判断可能存在的字符串时使用的条件是:没有连续的操作符
但是在判断例如:1+2+3+ 的表达式时,就没办法正确处理最后一个字符,最后保存的表达式可能就是 1+2+3+ ,这明显不正确,因此设置 if 条件来处理可能的表达式。

4. 为什么在 calculateFormula 中将 String 类型的数值转换为 Long 类型?

具体代码:

for (int i = 0; i < list.size(); i++) {
	if (!list.get(i).equals("+") && !list.get(i).equals("*") && !list.get(i).equals("-")) {
		long num = Long.parseLong(list.get(i));
		list.set(i, num + "");
	}
}

这是因为在测试用例中有输入类似: 0200 的情况,这样输出的结果为:0200,不符合要求,故进行处理。
注意:在遇到 0200 + 1 的情况无论有无这段代码都不会出现 0201 的情况。

5. 为什么只通过了88.9%的测试用例?

这是因为测试用例本身就有问题,在测试用例中有如此两个用例:
用例编号: test_16
用例一: -02
预期输出: 0

用例编号: test_15
用例二: 12345
预期输出: 12345

这两个用例之间存在逻辑矛盾,对于 -02 ,如果理想算法认为负数合法的话,预期输出应为: -2 ,但是并不是,这说明在判断表达式是否合法的条件上有一条,即:操作符的两侧必有数字以构成表达式,否则非法;同时,也说明了不能将数值单独作为表达式,否则的话预期输出就应当为:2。但是,很明显的是,结合用例二,我们可以知道单独的数值也能作为表达式,如此一来,两个用例之间自相矛盾,无法同时处理。

测试

OJ算法

题目编号结果内存时间语言代码长度
3005答案错误 AC:88.9%36904 KiB2248 msJava/Edit4619 bytes

侵权必删声明
本资料部分内容来源于互联网及公开渠道,仅供学习和交流使用,版权归原作者所有。若涉及版权问题,敬请原作者联系我,我将立即处理或删除相关内容。感谢您的理解与支持。

  • 20
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值