【刷穿 LeetCode】282. 给表达式添加运算符 : 一道利用「代数系统」的回溯题

简介: 【刷穿 LeetCode】282. 给表达式添加运算符 : 一道利用「代数系统」的回溯题

网络异常,图片无法展示
|


题目描述



这是 LeetCode 上的 282. 给表达式添加运算符 ,难度为 困难


Tag : 「DFS」、「数学」


给定一个仅包含数字 0-9 的字符串 num 和一个目标值整数 target ,在 num 的数字之间添加 二元 运算符(不是一元)+-* ,返回所有能够得到目标值的表达式。


示例 1:


输入: num = "123", target = 6
输出: ["1+2+3", "1*2*3"] 
复制代码


示例 2:


输入: num = "232", target = 8
输出: ["2*3+2", "2+3*2"]
复制代码


示例 3:


输入: num = "105", target = 5
输出: ["1*0+5","10-5"]
复制代码

示例 4:


输入: num = "00", target = 0
输出: ["0+0", "0-0", "0*0"]
复制代码


示例 5:


输入: num = "3456237490", target = 9191
输出: []
复制代码


提示:


  • 1 <= num.length <= 10
  • num 仅含数字
  • -2^{31} <= target <= 2^{31} - 1231<=target<=2311


回溯算法



最开始的想法是先使用 DFS 搜出来所有的表达式,然后套用 (题解)227. 基本计算器 II 方案,计算所有表达式的结果,并将计算结果为 targettarget 的表达式加到结果集。


假设原字符串 numnum 的长度为 nn,由于每个位置之间存在四种插入决策(不插入符号、+-*),共有 n - 1n1 个位置需要决策,因此搜索所有表达式的复杂度为 O(4^{n - 1})O(4n1);同时需要对所有的表达式执行计算,复杂度为 O(n)O(n),整体复杂度为 O(n * 4^{n - 1})O(n4n1)


添加运算符后的表达式长度不会超过 2020,因此总的计算量应该是在 10^7107 以内,但可能是因为常数问题超时了(各种优化双栈操作也还是 TLE,在这上面浪费了好多时间 QWQ)。


因此,我们需要考虑在搜索过程中进行计算,以避免使用 (题解)227. 基本计算器 II 这种常数较大的计算方式。


我们考虑如果只有 +- 的话,可以很容易将运算和回溯搜索所有表达进行结合。但当存在 * 时,由于存在运算优先级的问题,我们需要记录形如 a + b * c 中的乘法部分。


实现上,除了记录当前决策到原串 numnum 的哪一位 uu,以及当前的运算结果 curcur 以外,还需要额外记录最后一次的计算结果 prevprev,然后在决策表达式中的第 kk 个部分时,对本次添加的运算符合做分情况讨论:


  • 如果本次添加的 + 操作,且第 kk 项的值是 nextnext:那么直接使用 cur + nextcur+next 来更新 curcur,同时 nextnext 作为下一次的 prevprev
  • 如果本次添加的 - 操作,且第 kk 项的值是 nextnext:同理,那么直接使用 cur - nextcurnext 来更新 curcur,同时 -nextnext 作为下一次的 prevprev
  • 如果本次添加的 * 操作,且第 kk 项的值是 nextnext:此时需要考虑运算符的优先级问题,由于本次的 nextnext 是与上一次的操作数 prevprev 执行乘法,而 curcur 已经累加了 prevprev 的影响,因此需要先减去 prevprev,再加上 prev * nextprevnext,以此来更新 curcur,同时 prev * nextprevnext 也作为下一次的 prevprev


一些细节:需要注意前导零(00 单独作为一位是被允许的,但是多位且首部为 00 是不允许的)以及 +- 不作为一元运算符(运算符不能出现在表达式的首部)的情况。


代码:


class Solution {
    List<String> ans = new ArrayList<>();
    String s;
    int n, t;
    public List<String> addOperators(String num, int target) {
        s = num;
        n = s.length();
        t = target;
        dfs(0, 0, 0, "");
        return ans;
    }
    void dfs(int u, long prev, long cur, String ss) {
        if (u == n) {
            if (cur == t) ans.add(ss);
            return ;
        }
        for (int i = u; i < n; i++) {
            if (i != u && s.charAt(u) == '0') break;
            long next = Long.parseLong(s.substring(u, i + 1));
            if (u == 0) {
                dfs(i + 1, next, next, "" + next);
            } else {
                dfs(i + 1,  next, cur + next, ss + "+" + next);
                dfs(i + 1, -next, cur - next, ss + "-" + next);
                long x = prev * next;
                dfs(i + 1, x, cur - prev + x, ss + "*" + next);
            }
        }
    }
}
复制代码


  • 时间复杂度:O(4^{n})O(4n)
  • 空间复杂度:O(4^{n})O(4n)


总结



该做法表面上,只是实现了边搜索边计算的功能,但其本质上是利用了实数集 SS 和运算符 +- 的本质也是 +)和 * 能够组成代数系统。


利用代数系统 (S, +, *)(S,+,),我们可以确保运算过程中的任意一个中间结果,都能使用形如 a + b * c 的形式进行表示,因此我们只需要多维护一个后缀串结果即可。


而代数系统的相关知识,以及如何确定能否作为一个代数系统,有兴趣的同学可以尝试去了解一下。


最后



这是我们「刷穿 LeetCode」系列文章的第 No.282 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
存储 索引 Python
leetcode-350:两个数组的交集 II(python中Counter的用法,海象运算符:=)
leetcode-350:两个数组的交集 II(python中Counter的用法,海象运算符:=)
127 0
|
3月前
|
算法 Go 索引
【LeetCode 热题100】回溯:括号生成 & 组合总和(力扣22 / 39 )(Go语言版)
本文深入解析了LeetCode上的两道经典回溯算法题:**22. 括号生成**与**39. 组合总和**。括号生成通过维护左右括号数量,确保路径合法并构造有效组合;组合总和则允许元素重复选择,利用剪枝优化搜索空间以找到所有满足目标和的组合。两者均需明确路径、选择列表及结束条件,同时合理运用剪枝策略提升效率。文章附有Go语言实现代码,助你掌握回溯算法的核心思想。
96 0
|
算法
代码随想录Day21 回溯 LeetCodeT216 组合总和III LeetCode T17电话号码的字母总和
代码随想录Day21 回溯 LeetCodeT216 组合总和III LeetCode T17电话号码的字母总和
90 0
|
机器学习/深度学习 存储 算法
Python5种算法回溯+剪枝、字典序、递归交换、计数回溯、迭代法 实现全排列ll【力扣题47】
Python5种算法回溯+剪枝、字典序、递归交换、计数回溯、迭代法 实现全排列ll【力扣题47】
|
算法 数据挖掘 开发者
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
|
机器学习/深度学习 存储 算法
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
|
SQL
leetcode-SQL-1440. 计算布尔表达式的值
leetcode-SQL-1440. 计算布尔表达式的值
112 1
leetcode-1106: 解析布尔表达式
leetcode-1106: 解析布尔表达式
110 0
|
索引 容器
leetcode-6130:设计数字容器系统
leetcode-6130:设计数字容器系统
82 0