华为OD机试 - 提取字符串中的最长合法简单数学表达式(Java & JS & Python & C)

这篇具有很好参考价值的文章主要介绍了华为OD机试 - 提取字符串中的最长合法简单数学表达式(Java & JS & Python & C)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题目描述

提取字符串中的最长合法简单数学表达式,字符串长度最长的,并计算表达式的值。如果没有,则返回 0 。

简单数学表达式只能包含以下内容:

  • 0-9数字,符号+-*

说明:

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

输入描述

字符串

输出描述

表达式值

用例

输入 1-2abcd
输出 -1
说明 最长合法简单数学表达式是"1-2",结果是-1

题目解析

注意!!!本题原题描述中没有 / 除号

华为 od 最长合法的表达式,华为OD机试ABC+OJ(Java & JS & Py),算法,华为机试,Java,JavaScript,Python,C语言

因此,本题的合法表达式不需要考虑 '/' 号,也就不用考虑除0,以及除法是整除还是小数除的问题。

另外,本题的 +、-号仅作为运算符号,不作为正负号。因此 "+1","-1" 这种不能理解为合法的表达式。


本题可以分为两步求解:

  1. 找出输入串中最长合法的表达式
  2. 计算最长合法表达式的结果

关于1的求解,有两种思路:

  • 双指针
  • 正则匹配

其中正则匹配实现起来比较简单,用于匹配合法表达式的正则也不是很难写,对应正则解析如下:

华为 od 最长合法的表达式,华为OD机试ABC+OJ(Java & JS & Py),算法,华为机试,Java,JavaScript,Python,C语言

对于python而言,为了更好地适配findall方法,我们可以对上面正则表达式中内层括号使用到非捕获组

华为 od 最长合法的表达式,华为OD机试ABC+OJ(Java & JS & Py),算法,华为机试,Java,JavaScript,Python,C语言


关于2的求解

对于JS和Python而言,可以使用内置的eval函数计算字符串表达式的结果。

更常规的思路是利用栈结构:

找出最长合法表达式子串后,比如 "1-2*3+10+2",我们需要注意表达式运算符优先级问题,即先乘,后加减,相同优先级的运算从左到右进行。

这里我的思路是将 合法表达式串 进行分块,比如上面表达式可以分为:

  • 1
  • -2*3
  • 10
  • 2

我们可以发现:

  • +、-运算符前面的操作数都是独立成块,比如上面表达式的操作数1,10
  • * 运算符前面的操作数则需要组合成块,比如上面表达式的操作数2后面是 * 运算符,因此 2 需要和 3 进行组合。

分块之后,我们只需要求各块结果之和即可。

具体逻辑实现如下:

  • 首先定义一个栈stack,用于保存各个块的结果;
  • 其次定义一个块的值容器numStr,用于临时缓存分块的值;
  • 最后定义一个块的系数变量numCoef,用于临时缓存分块的系数;

扫描合法表达式串,如果当前扫描的字符c是:

  • c 是数字,则直接缓存进块的值容器numStr中
  • c 是 + 号,则打断前一个操作数的收集,此时我们应该将前一个操作数计算结果后加入到stack中,操作数 = int(numStr) * numCoef,同时更新numCoef = 1,因为c是+号,所以后一个操作数的系数为1
  • c 是 - 号,则打断前一个操作数的收集,此时我们应该将前一个操作数计算结果后加入到stack中,操作数 = int(numStr) * numCoef,同时更新numCoef = -1,因为c是-号,所以后一个操作数的系数为-1
  • c 是 * 号,则打断前一个操作数的收集,并且 * 前后的两个操作数需要组合,而 * 前面的操作数记录在stack栈顶中,我们可以取出stack栈顶值 记录到 numCoef 中,即 * 前面的操作数,可以当初 * 后面操作数的系数

这块实现的更详细解析,可以参考:

华为OD机试 - 符号运算(Java & JS & Python & C)_java 华为od机试,符号运算-CSDN博客


2024.01.29

本题按照上面解法,有考友反馈只能拿15%通过率,实在想不通是哪里还有问题,感觉上面思路已经很完美了。

想了一个晚上,唯一可能的就是合法表达式的判断,本题没有直接说明合法表达式的限制规则,只是说:

  • 简单数学表达式只能包含:0-9数字,符号+-*
  • 操作符不能连续出现,如 +--+1 是不合法的

其他的限制就没有了。

按照上面解法的正则匹配

华为 od 最长合法的表达式,华为OD机试ABC+OJ(Java & JS & Py),算法,华为机试,Java,JavaScript,Python,C语言

如果输入串是:"+1+2m2222"

则该正则会依次匹配出合法表达式"1+2" 和 ”2222“,由于2222的长度更长,因此最长合法表达式是"2222",计算结果也是2222。

但是,我们根据题目对合法表达式的限制规则来看,其实:"+1+2" 也是符合要求的:

  • 该表达式中只能包含:0-9数字,符号+-*
  • 该表达式中没有出现连续操作符

那要是这样说的话,如果输入串是:"+1+2+m22222"

那么 "+1+2+" 是不是也算符合题目要求的合法表达式呢。。。。。

我滴个乖,这个出题真是太不严谨了。。。。

我更新了下面解法,让其可以完成 "+1+2" 的合法表达式识别,但是我不认为  "+1+2+" 是合法表达式,所以没有适配。

网上搜了一圈,找到一个100%的python解法,但是感觉不对,有需要的可以找我要。文章来源地址https://www.toymoban.com/news/detail-788153.html

JS算法源码

正则+栈解法
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  const s = await readline();
  console.log(getResult(s));
})();

function getResult(s) {
  const maxLenExp = getMaxLenExp(s);

  if (maxLenExp.length == 0) {
    return 0;
  } else {
    return calcExpStr(maxLenExp);
  }
}

function getMaxLenExp(s) {
  // 下面正则无法匹配这样的数字串:+1+2
  // const reg = /((\d+[+*-])*\d+)/g;

  // 下面正则可以匹配到这样的数字串:+1+2
  const reg = /([+-]?(\d+[+*-])*\d+)/g;

  let maxLenExp = "";

  let res;
  while ((res = reg.exec(s)) != null) {
    const exp = res[0];

    if (exp.length > maxLenExp.length) {
      maxLenExp = exp;
    }
  }

  return maxLenExp;
}

function calcExpStr(exp) {
  // 这里在表达式结尾追加"+0"是为了避免后面收尾操作,不理解的话,可以去掉此步,测试下"1-2"
  exp += "+0";

  // 记录表达式中各块的操作数
  const stack = [];
  // 各块操作数的"值"部分的缓存容器
  let numStr = [];

  // 各块操作数的"系数"部分,默认为1
  let num_coef = 1;

  // 如果合法的表达式可以+或-开头
  const start = exp[0];
  if (start == "+" || start == "-") {
    // 将exp开头的符号去掉
    exp = exp.slice(1);
  }

  if (start == "-") {
    // 如果表达式开头是负号,则开头操作数的系数为-1
    num_coef = -1;
  }

  // 处理剩余表达式
  for (let c of exp) {
    if (c >= "0" && c <= "9") {
      numStr.push(c);
      continue;
    }

    // 如果扫描到的字符c是运算符,那么该运算符打断了前面操作数的扫描,前面操作数 = 系数 * 值
    const num = num_coef * parseInt(numStr.join(""));
    stack.push(num);

    // 清空缓存容器,用于下一个操作数的”值“记录
    numStr = [];

    switch (c) {
      case "+":
        // 如果运算符是加法,则后一个操作数的系数为1
        num_coef = 1;
        break;
      case "-":
        // 如果运算符是减法,则后一个操作数的系数为-1
        num_coef = -1;
        break;
      case "*":
        // 如果运算符是乘法,则后一个操作数的系数为栈顶值,比如2*3,其中2可以当作3的系数
        num_coef = stack.pop();
        break;
    }
  }

  // 表达式分块后,每一块独立计算,所有块的和就是表达式的结果
  let res = 0;
  for (let num of stack) {
    res += num;
  }

  return res;
}
正则+eval解法
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  const s = await readline();
  console.log(getResult(s));
})();

function getResult(s) {
  const maxLenExp = getMaxLenExp(s);

  if (maxLenExp.length == 0) {
    return 0;
  } else {
    return eval(maxLenExp);
  }
}

function getMaxLenExp(s) {
  // 下面正则无法匹配这样的数字串:+1+2
  // const reg = /((\d+[+*-])*\d+)/g;

  // 下面正则可以匹配到这样的数字串:+1+2
  const reg = /([+-]?(\d+[+*-])*\d+)/g;

  let maxLenExp = "";

  let res;
  while ((res = reg.exec(s)) != null) {
    const exp = res[0];

    if (exp.length > maxLenExp.length) {
      maxLenExp = exp;
    }
  }

  return maxLenExp;
}

Java算法源码

正则+栈解法
import java.util.LinkedList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println(getResult(sc.nextLine()));
  }

  public static long getResult(String s) {
    String maxLenExp = getMaxLenExp(s);

    if (maxLenExp.length() == 0) {
      return 0;
    } else {
      return calcExpStr(maxLenExp);
    }
  }

  public static String getMaxLenExp(String s) {
    // 下面正则无法匹配这样的数字串:+1+2
    //    Matcher matcher = Pattern.compile("((\\d+[+*-])*\\d+)").matcher(s);

    // 下面正则可以匹配到这样的数字串:+1+2
    Matcher matcher = Pattern.compile("([+-]?(\\d+[+*-])*\\d+)").matcher(s);

    String maxLenExp = "";

    while (matcher.find()) {
      String exp = matcher.group(0);

      if (exp.length() > maxLenExp.length()) {
        maxLenExp = exp;
      }
    }

    return maxLenExp;
  }

  public static long calcExpStr(String exp) {
    // 这里在表达式结尾追加"+0"是为了避免后面收尾操作,不理解的话,可以去掉此步,测试下"1-2"
    exp += "+0";

    // 记录表达式中各块的操作数
    LinkedList<Long> stack = new LinkedList<>();
    // 各块操作数的"值"部分的缓存容器
    StringBuilder numStr = new StringBuilder();

    // 各块操作数的"系数"部分,开头的操作数系数默认为1
    long num_coef = 1;

    // 如果合法的表达式可以+或-开头
    char start = exp.charAt(0);

    if (start == '+' || start == '-') {
      // 将exp开头的符号去掉
      exp = exp.substring(1);
    }

    if (start == '-') {
      // 如果表达式开头是负号,则开头操作数的系数为-1
      num_coef = -1;
    }

    // 处理剩余表达式
    for (int i = 0; i < exp.length(); i++) {
      char c = exp.charAt(i);

      if (c >= '0' && c <= '9') {
        numStr.append(c);
        continue;
      }

      // 如果扫描到的字符c是运算符,那么该运算符打断了前面操作数的扫描,前面操作数 = 系数 * 值
      long num = num_coef * Long.parseLong(numStr.toString());
      stack.add(num);

      // 清空缓存容器,用于下一个操作数的”值“记录
      numStr = new StringBuilder();

      switch (c) {
        case '+':
          // 如果运算符是加法,则后一个操作数的系数为1
          num_coef = 1;
          break;
        case '-':
          // 如果运算符是减法,则后一个操作数的系数为-1
          num_coef = -1;
          break;
        case '*':
          // 如果运算符是乘法,则后一个操作数的系数为栈顶值,比如2*3,其中2可以当作3的系数
          num_coef = stack.removeLast();
          break;
      }
    }

    // 表达式分块后,每一块独立计算,所有块的和就是表达式的结果
    long res = 0;
    for (long num : stack) {
      res += num;
    }

    return res;
  }
}

Python算法源码

正则+栈解法
# 输入获取
import re

s = input()


# 计算合法表达式的结果
def calcExpStr(exp):
    # 这里在表达式结尾追加"+0"是为了避免后面收尾操作,不理解的话,可以去掉此步,测试下"1-2"
    exp += '+0'

    # 记录表达式中各块的操作数
    stack = []
    # 各块操作数的"值"部分的缓存容器
    numStr = []

    # 各块操作数的"系数"部分,默认为1
    num_coef = 1

    # 如果合法的表达式可以+或-开头
    start = exp[0]
    if start == '+' or start == '-':
        # 将exp开头的符号去掉
        exp = exp[1:]

    if start == '-':
        # 如果表达式开头是负号,则开头操作数的系数为-1
        num_coef = -1

    # 处理剩余表达式
    for c in exp:
        if '9' >= c >= '0':
            numStr.append(c)
            continue

        # 如果扫描到的字符c是运算符,那么该运算符打断了前面操作数的扫描,前面操作数 = 系数 * 值
        num = num_coef * int("".join(numStr))
        stack.append(num)

        # 清空缓存容器,用于下一个操作数的”值“记录
        numStr.clear()

        if c == '+':
            # 如果运算符是加法,则后一个操作数的系数为1
            num_coef = 1
        elif c == '-':
            # 如果运算符是减法,则后一个操作数的系数为-1
            num_coef = -1
        elif c == '*':
            # 如果运算符是乘法,则后一个操作数的系数为栈顶值,比如2*3,其中2可以当作3的系数
            num_coef = stack.pop()

    # 表达式分块后,每一块独立计算,所有块的和就是表达式的结果
    return sum(stack)


# 获取最长合法表达式
def getMaxLenExp():
    # 下面正则无法匹配这样的数字串:+1+2
    # lst = re.compile(r"((?:\d+[+*-])*\d+)").findall(s)

    # 下面正则可以匹配到这样的数字串:+1+2
    lst = re.compile(r"([+-]?(?:\d+[+*-])*\d+)").findall(s)

    maxLenExp = ""

    for exp in lst:
        if len(exp) > len(maxLenExp):
            maxLenExp = exp

    return maxLenExp


# 算法入口
def getResult():
    maxLenExp = getMaxLenExp()

    if len(maxLenExp) == 0:
        return 0
    else:
        return calcExpStr(maxLenExp)


# 算法调用
print(getResult())
正则+eval解法
# 输入获取
import re

s = input()


# 获取最长合法表达式
def getMaxLenExp():
    # 下面正则无法匹配这样的数字串:+1+2
    # lst = re.compile(r"((?:\d+[+*-])*\d+)").findall(s)

    # 下面正则可以匹配到这样的数字串:+1+2
    lst = re.compile(r"([+-]?(?:\d+[+*-])*\d+)").findall(s)

    maxLenExp = ""

    for exp in lst:
        if len(exp) > len(maxLenExp):
            maxLenExp = exp

    return maxLenExp


# 算法入口
def getResult():
    maxLenExp = getMaxLenExp()

    if len(maxLenExp) == 0:
        return 0
    else:
        return eval(maxLenExp)


# 算法调用
print(getResult())

C算法源码

双指针+栈解法
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_SIZE 10000
#define OPERATOR_NUM_LEN 100
#define OPERATOR_NUM_SIZE 1000

char s[MAX_SIZE] = {'\0'};

long calcExpStr(const char *exp) {
    // 记录表达式中各块的操作数
    long stack[OPERATOR_NUM_SIZE];
    int stack_size = 0;

    // 各块操作数的"值"部分的缓存容器
    char numStr[OPERATOR_NUM_LEN] = {'\0'};
    int numStr_size = 0;

    // 各块操作数的"系数"部分,默认为1
    long num_coef = 1;

    // 如果合法的表达式可以+或-开头
    char start = exp[0];
    if(start == '+' || start == '-') {
        // 将exp开头的符号去掉
        exp += 1;
    }

    if(start == '-') {
        // 如果表达式开头是负号,则开头操作数的系数为-1
        num_coef = -1;
    }


    int i = 0;
    while (exp[i] != '\0') {
        char c = exp[i];

        if (c >= '0' && c <= '9') {
            numStr[numStr_size++] = c;
        } else {
            // 如果扫描到的字符c是运算符,那么该运算符打断了前面操作数的扫描,前面操作数 = 系数 * 值
            long num = num_coef * atol(numStr);
            stack[stack_size++] = num;

            // 清空缓存容器,用于下一个操作数的”值“记录
            numStr_size = 0;
            memset(numStr, '\0', OPERATOR_NUM_LEN);

            if (c == '+') {
                // 如果运算符是加法,则后一个操作数的系数为1
                num_coef = 1;
            } else if (c == '-') {
                // 如果运算符是减法,则后一个操作数的系数为-1
                num_coef = -1;
            } else if (c == '*') {
                // 如果运算符是乘法,则后一个操作数的系数为栈顶值,比如2*3,其中2可以当作3的系数
                num_coef = stack[--stack_size];
            }
        }

        i++;
    }

    // 收尾处理
    if (numStr_size > 0) {
        long num = num_coef * atol(numStr);
        stack[stack_size++] = num;
    }

    // 表达式分块后,每一块独立计算,所有块的和就是表达式的结果
    long res = 0;
    for (int j = 0; j < stack_size; j++) {
        res += stack[j];
    }

    return res;
}

int isDigit(char c) {
    return c >= '0' && c <= '9';
}

int isOperator(char c) {
    return c == '+' || c == '-' || c == '*';
}

int main() {
    gets(s);

    // 记录最长合法表达式的长度
    int maxLen = 0;
    // 记录最长合法表达式的起始位置
    int start = -1;

    // 双指针找最长合法表达式
    int l = 0;
    int r = 0;

    while (s[r] != '\0') {
        // 合法表达式必须以数字开头
        // 如果+-开头的表达式也是合法的表达式,则需要添加当前代码135~138行逻辑
        while (s[l] != '\0' && !isDigit(s[l])) {
            l++;
        }

        if (s[l] == '\0') {
            break;
        }

        // l找到合法表达式的开头后,r指针扫开始描合法表达式剩余部门
        r = l + 1;

        while (s[r] != '\0') {
            // 如果r指针扫描到的是数字字符,则继续扫描
            // 如果r指针扫描到的是+-*符号,则需要看r-1字符是不是+-*符号,如果不是则继续扫描
            if (isDigit(s[r]) || (isOperator(s[r]) && !isOperator(s[r - 1]))) {
                r++;
            } else {
                // 其他情况中断r扫描
                break;
            }
        }

        // 此时 [l, r) 左闭右开范围就是合法表达式
        int len = r - l;

        // 注意如果r-1位置是+-*符号,则不能包含
        if (isOperator(s[r - 1])) {
            len -= 1;
        }

        // 如果认为 +1+2,-2*3 这种也是合法的表达式,则需要加入下面逻辑
        if (l >= 1 && (s[l - 1] == '+' || s[l - 1] == '-')) {
            l--;
            len++;
        }

        // 记录最长的合法表达式长度,以及起始位置
        if (len > maxLen) {
            maxLen = len;
            start = l;
        }

        // 找到一个合法表达式后,继续找下一个合法表达式
        l = r + 1;
    }

    if (start == -1) {
        // 如果没找到合法表达式,则直接打印0
        puts("0");
    } else {
        // 找到最长合法表达式,则计算其结果打印
        s[start + maxLen] = '\0';
        printf("%ld\n", calcExpStr(s + start));
    }

    return 0;
}

到了这里,关于华为OD机试 - 提取字符串中的最长合法简单数学表达式(Java & JS & Python & C)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 华为OD机试真题 Java 实现【输出指定字母在字符串的中的索引】【2023 B卷 100分】,附详细解题思路

    华为OD机试 2023B卷题库疯狂收录中,刷题 点这里

    2024年02月15日
    浏览(52)
  • 华为OD机试 - 字符串摘要(Java & JS & Python)

    题目描述 给定一个字符串的摘要算法,请输出给定字符串的摘要值 去除字符串中非字母的符号。 如果出现连续字符(不区分大小写) ,则输出:该字符 (小写) + 连续出现的次数。 如果是非连续的字符(不区分大小写),则输出:该字符(小写) + 该字母之后字符串中出现的该字符

    2024年02月14日
    浏览(44)
  • 华为OD机试 - 字符串划分(Java & JS & Python)

    题目描述 给定一个小写字母组成的字符串 s,请找出字符串中两个不同位置的字符作为分割点,使得字符串分成三个连续子串且子串权重相等,注意子串不包含分割点。 若能找到满足条件的两个分割点,请输出这两个分割点在字符串中的位置下标,若不能找到满足条件的分

    2024年02月11日
    浏览(59)
  • 华为OD机试 - 字符串拼接(Java & JS & Python & C)

    题目描述 给定 M(0 M ≤ 30)个字符(a-z),从中取出任意字符(每个字符只能用一次)拼接成长度为 N(0 N ≤ 5)的字符串, 要求相同的字符不能相邻,计算出给定的字符列表能拼接出多少种满足条件的字符串, 输入非法或者无法拼接出满足条件的字符串则返回0。 输入描

    2024年01月22日
    浏览(77)
  • 【满分】【华为OD机试真题2023 JAVA&JS】字符串重新排序

    知识点排序数组  时间限制:1s 空间限制:256MB 限定语言:不限 给定一个字符串s,s包含以空格分隔的若干个单词,请对s进行如下处理后输出: 1、单词内部调整:对每个单词字母重新按字典序排序; 2、单词间顺序调整:     1)统计每个单词出现

    2023年04月23日
    浏览(62)
  • 华为OD机试 - 字符串化繁为简(Java & JS & Python)

    题目描述 给定一个输入字符串,字符串只可能由英文字母( \\\'a\\\' ~ \\\'z\\\'、\\\'A\\\' ~ \\\'Z\\\' )和左右小括号( \\\'(\\\'、\\\')\\\' )组成。 当字符里存在小括号时,小括号是成对的,可以有一个或多个小括号对,小括号对不会嵌套,小括号对内可以包含1个或多个英文字母,也可以不包含英文字母。

    2024年02月09日
    浏览(52)
  • 【华为OD机试 】数字字符串组合倒序(C++ Java JavaScript Python)

    华为OD统一考试A卷+B卷 新题库说明 2023年5月份,华为官方已经将的 2022/0223Q(1/2/3/4)统一修改为OD统一考试(A卷)和OD统一考试(B卷)。 你收到的链接上面会标注A卷还是B卷。请注意:根据反馈,目前大部分收到的都是B卷。但是仍有概率抽到A卷。 A卷对应2023的新题库(2022Q4 2

    2024年02月05日
    浏览(50)
  • 华为OD机试 - 分割均衡字符串(Java & JS & Python & C & C++)

    题目描述 均衡串定义:字符串中只包含两种字符,且这两种字符的个数相同。 给定一个均衡字符串,请给出可分割成新的均衡子串的最大个数。 约定:字符串中只包含大写的 X 和 Y 两种字符。 输入描述 输入一个均衡串。 字符串的长度:[2, 10000]。 给定的字符串均为均衡字

    2024年03月14日
    浏览(59)
  • 【华为OD机试】第K长字符串(python, java, c++, js)

    前言 :本专栏将持续更新华为OD机试题目,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于OD机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:nansun0903@163.com;备注:CSDN。 给定一个字符串,只包含大写

    2024年02月11日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包