饭后小甜点leetcode——字符串模式匹配与表达式解析

文章目录

    • 模式匹配
      • 正则表达式匹配
      • 通配符匹配
    • 表达式解析
      • 字符串转换整数 (atoi)
      • 基本计算器
      • 基本计算器 II
      • 基本计算器 III
      • 逆波兰表达式求值
      • 为运算表达式设计优先级
      • 给表达式添加运算符
      • 三元表达式解析器
      • 删除注释
      • 迷你语法分析器
      • Lisp 语法解析
      • 原子的数量

模式匹配

正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:

输入:
s = “ab”
p = “."
输出: true
解释: ".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。
示例 4:

输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:

输入:
s = “mississippi”
p = “misisp*.”
输出: false

链接:https://leetcode-cn.com/problems/regular-expression-matching

【思路】先从基本的递归考虑,然后再优化到动态规划。
【代码1:基本递归】这种解法复杂度很高,只是为了更好的理解DP解法。

public bool IsMatch(string text, string pattern) {
    if (string.IsNullOrEmpty(pattern)) return string.IsNullOrEmpty(text);
    //先看第一个字符能不能匹配上
    var first_match = (!string.IsNullOrEmpty(text) &&
                           (pattern[0] == text[0] || pattern[0] == '.'));
	//如果pattern的长度大于2而且第二个字符是*号
    if (pattern.Length >= 2 && pattern[1] == '*'){
    	//*代表零个前一个字符时直接看text和substring(2)是否匹配
        return (IsMatch(text, pattern.Substring(2)) ||
        	//*代表一个或多个前一个字符时,且第一个字符匹配上了,去掉text第一个字符直接跟pattern比较
                (first_match && IsMatch(text.Substring(1), pattern)));
    } else {
    	//如果pattern第二个字符不是*号,那么就同时看第一个字符是否匹配上,且去掉他们两个第一个字符再看是否匹配
        return first_match && IsMatch(text.Substring(1), pattern.Substring(1));
    }
}

【代码2:动态规划】时间复杂度为 O(TP),空间复杂度为O(TP)

public bool IsMatch(string text, string pattern) {
    var dp = new bool[text.Length + 1, pattern.Length + 1];
    dp[text.Length, pattern.Length] = true;

    for (int i = text.Length; i >= 0; i--){
        for (int j = pattern.Length - 1; j >= 0; j--){
            var first_match = (i < text.Length &&
                                   (pattern[j] == text[i] ||
                                    pattern[j] == '.'));
            if (j + 1 < pattern.Length && pattern[j+1] == '*'){
                dp[i, j] = dp[i, j+2] || first_match && dp[i+1, j];
            } else {
                dp[i, j] = first_match && dp[i+1, j+1];
            }
        }
    }
    return dp[0, 0];
}

通配符匹配

给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。

‘?’ 可以匹配任何单个字符。
‘*’ 可以匹配任意字符串(包括空字符串)。//注意此处和前一个题不太一样
两个字符串完全匹配才算匹配成功。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
示例 1:

输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:
s = “aa”
p = “*”
输出: true
解释: ‘*’ 可以匹配任意字符串。
示例 3:

输入:
s = “cb”
p = “?a”
输出: false
解释: ‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。
示例 4:

输入:
s = “adceb”
p = “*a*b”
输出: true
解释: 第一个 ‘*’ 可以匹配空字符串, 第二个 ‘*’ 可以匹配字符串 “dce”.
示例 5:

输入:
s = “acdcb”
p = “a*c?b”
输入: false

链接:https://leetcode-cn.com/problems/wildcard-matching

public bool IsMatch(string s, string p) {
    int m = s.Length, n = p.Length;
    var f = new bool[m + 1, n + 1];
    
    f[0, 0] = true;
    for(int i = 1; i <= n; i++){
        f[0, i] = f[0, i - 1] && p[i - 1] == '*';
    }
    
    //注意此处从1到结尾,所以i-1对应dp中的i,j-1对应dp中的j
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            if(s[i - 1] == p[j - 1] || p[j - 1] == '?'){
                f[i, j] = f[i - 1, j - 1];
            }
            if(p[j - 1] == '*'){
                f[i, j] = f[i, j - 1] || f[i - 1, j];
            }
        }
    }
    return f[m, n];
}

表达式解析

字符串转换整数 (atoi)

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: “42”
输出: 42
示例 2:

输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:

输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:

输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:

输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

链接:https://leetcode-cn.com/problems/string-to-integer-atoi

public int MyAtoi(string str) {
    var sign = 1; var bas = 0; var i = 0;//bas=base
    //1. Trim + 判断null
    str = str.Trim();
    if(string.IsNullOrEmpty(str)) return 0;
    //2. 记录Sign
    if (str[i] == '-' || str[i] == '+') {
        sign = str[i] == '-'?-1:1;
        i++;
    }

    while (i<str.Length && str[i] >= '0' && str[i] <= '9') {
        //3.还没加上新来的字符是就已经比max/10大了,加上新来的就一定会溢出
        //4.没加上新来的字符时刚好等于max/10,如果新来的大于等于8,正数会溢出,负数如果大于8溢出,但最后反正正数溢出按max,负数溢出按min,所以刚好
        if (bas >  int.MaxValue / 10 || (bas == int.MaxValue / 10 && str[i] - '0' >= 8)) {
            return sign == 1? int.MaxValue : int.MinValue;
        }
        bas  = 10 * bas + (str[i++] - '0');

    }

    return bas * sign;
}

基本计算器

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。

示例 1:

输入: “1 + 1”
输出: 2
示例 2:

输入: " 2-1 + 2 "
输出: 3
示例 3:

输入: “(1+(4+5+2)-3)+(6+8)”
输出: 23
说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。

链接:https://leetcode-cn.com/problems/basic-calculator

public int Calculate(string s) {
    var nums = new Stack<int>();
    var ops = new Stack<int>();
    int num = 0;
    int rst = 0;
    int sign = 1;
    foreach (char c in s) { 
        if(c==' ') continue;
        else if (c>='0'&&c<='9') {
            num = num * 10 + c - '0';
        }
        else {
            rst += sign * num;
            num = 0;
            if (c == '+') sign = 1;
            if (c == '-') sign = -1;
            if (c == '(') {
                nums.Push(rst);
                ops.Push(sign);
                rst = 0;
                sign = 1;
            }
            if (c == ')' && ops.Count!=0) {
                rst = ops.Pop() * rst + nums.Pop();
            }
        }
    }
    rst += sign * num;
    return rst;
}

基本计算器 II

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:

输入: “3+2*2”
输出: 7
示例 2:

输入: " 3/2 "
输出: 1
示例 3:

输入: " 3+5 / 2 "
输出: 5
说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。

链接:https://leetcode-cn.com/problems/basic-calculator-ii

public int Calculate(string s) {
    int len = s.Length;
    if(s==null || len==0) return 0;
    var stack = new Stack<int>();
    int num = 0;
    char sign = '+';
    for(int i=0;i<len;i++){
        if(char.IsDigit(s[i])){
            num = num*10+s[i]-'0';
        }
        if((!char.IsDigit(s[i]) &&' '!=s[i]) || i==len-1){
            if(sign=='-'){
                stack.Push(-num);
            }
            if(sign=='+'){
                stack.Push(num);
            }
            if(sign=='*'){
                stack.Push(stack.Pop()*num);
            }
            if(sign=='/'){
                stack.Push(stack.Pop()/num);
            }
            sign = s[i];
            num = 0;
        }
    }

    int re = 0;
    foreach(int i in stack){
        re += i;
    }
    return re;
}

基本计算器 III

实现一个基本的计算器来计算简单的表达式字符串。

表达式字符串可以包含左括号 ( 和右括号 ),加号 + 和减号 -,非负 整数和空格 。

表达式字符串只包含非负整数, +, -, *, / 操作符,左括号 ( ,右括号 )和空格 。整数除法需要向下截断。

你可以假定给定的字符串总是有效的。所有的中间结果的范围为 [-2147483648, 2147483647]。

一些例子:

“1 + 1” = 2
" 6-4 / 2 " = 4
“2*(5+52)/3+(6/2+8)" = 21
"(2+6
3+5- (3*14/7+2)*5)+3”=-12

注:不要 使用内置库函数 eval。

链接:https://leetcode-cn.com/problems/basic-calculator-iii

public class Solution {
    public int Calculate(string s) {
        var i = 0;
        return ParseExpr(s, ref i);
    }
    
    private int ParseExpr(string s, ref int i){
        var nums = new List<int>();
        char op = '+';
        int n = 0;
        for (; i < s.Length && op != ')'; ++i) {
            if (s[i] == ' ') {
                continue;
            }
            if (s[i] == '(') {
                i++;
                n = ParseExpr(s, ref i);
            }
            else if (char.IsDigit(s[i])){
                n = ParseNum(s, ref i);
            }
            switch(op) {
                case '+':
                    nums.Add(n);
                    break;
                case '-':
                    nums.Add(-n);
                    break;
                case '*':
                    nums[nums.Count-1] *= n;
                    break;
                case '/':
                    nums[nums.Count-1] /= n;
                    break;
            }
            if(i<s.Length) op = s[i];
        }
        int res = 0;
        foreach (int num in nums) {
            res += num;
        }
        return res;
    }
    
    private int ParseNum(string s, ref int i) {       // parse the num
        int n = 0;
        while (i < s.Length && char.IsDigit(s[i])) {
            n = 10 * n + s[i++] - '0';
        }
        return n;
    }
}

逆波兰表达式求值

根据逆波兰表示法,求表达式的值。

有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:

输入: [“2”, “1”, “+”, “3”, “*”]
输出: 9
解释: ((2 + 1) * 3) = 9
示例 2:

输入: [“4”, “13”, “5”, “/”, “+”]
输出: 6
解释: (4 + (13 / 5)) = 6
示例 3:

输入: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”, “+”]
输出: 22
解释:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

链接:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation

public int EvalRPN(string[] tokens) {
	int a,b;
	var S = new Stack<int>();
	foreach (var s in tokens) {
		if(s=="+") {
			S.Push(S.Pop()+S.Pop());
		}
		else if(s=="/") {
			b = S.Pop();
			a = S.Pop();
			S.Push(a / b);
		}
		else if(s=="*") {
			S.Push(S.Pop() * S.Pop());
		}
		else if(s=="-") {
			b = S.Pop();
			a = S.Pop();
			S.Push(a - b);
		}
		else {
			S.Push(int.Parse(s));
		}
	}	
	return S.Pop();
}

为运算表达式设计优先级

给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。

示例 1:

输入: “2-1-1”
输出: [0, 2]
解释:
((2-1)-1) = 0
(2-(1-1)) = 2
示例 2:

输入: “2*3-4*5”
输出: [-34, -14, -10, -10, 10]
解释:
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10

链接:https://leetcode-cn.com/problems/different-ways-to-add-parentheses

public class Solution {
    public IList<int> DiffWaysToCompute(string input)
    {
        var result = new List<int>();
        if (input == null || input.Length == 0)
        {
            return result;
        }
        for (var i = 0; i < input.Length; i++)
        {
            if (input[i] == '+' || input[i] == '-' || input[i] == '*')
            {
                var sub1 = input.Substring(0, i);
                var sub2 = input.Substring(i + 1);
                var list1 = DiffWaysToCompute(sub1);
                var list2 = DiffWaysToCompute(sub2);
                foreach(var l1 in list1)
                {
                    foreach(var l2 in list2)
                    {
                        switch (input[i])
                        {
                            case '+':
                                result.Add(l1 + l2);
                                break;
                            case '-':
                                result.Add(l1 - l2);
                                break;
                            case '*':
                                result.Add(l1 * l2);
                                break;
                        }
                    }
                }
            }
        }
        if (result.Count == 0)
        {
            result.Add(int.Parse(input));
        }
        return result;
    }
}

给表达式添加运算符

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

示例 1:

输入: num = “123”, target = 6
输出: [“1+2+3”, “123”]
示例 2:

输入: num = “232”, target = 8
输出: [“23+2", "2+32”]
示例 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
输出: []

链接:https://leetcode-cn.com/problems/expression-add-operators

public class Solution {
    public IList<string> AddOperators(string num, int target) {    
        var exprs = new List<string>();
        var preval = 0;
        var expr = new char[num.Length*2];
        var len = 0;
        Recurse(num, 0, 0, target, expr, len, preval, exprs);
        return exprs;
    }

    public void Recurse(string num, int index, long val, int target, char[] expr, int len, long preval, IList<string> exprs )
    {
        if(index == num.Length) {
            if (val == target) exprs.Add(new string(expr, 0, len));
            return;
        }
        long op2 = 0;
        int l = len;
        if(index != 0) len++;
        for(int i = index; i<num.Length; i++) {      
            op2 = op2 * 10 + num[i] - '0';
            expr[len++] = num[i];
            if (index == 0)
            {
                Recurse(num, i+1, op2, target, expr, len,  op2, exprs);
            }
            else
            {
                expr[l] = '+'; 
                Recurse(num, i+1, val + op2, target, expr, len, op2, exprs);

                expr[l] = '-'; 
                Recurse(num, i+1, val - op2, target, expr, len, -op2, exprs);

                expr[l] = '*'; 
                var val3 = val - preval + (preval * op2);
                Recurse(num, i+1, val3, target, expr, len, preval*op2, exprs);
            }
            if (num[index] == '0') break; 
        }
        return;
    }
}

三元表达式解析器

给定一个以字符串表示的任意嵌套的三元表达式,计算表达式的值。你可以假定给定的表达式始终都是有效的并且只包含数字 0-9, ?, :, T 和 F (T 和 F 分别表示真和假)。

注意:

给定的字符串长度 ≤ 10000。
所包含的数字都只有一位数。
条件表达式从右至左结合(和大多数程序设计语言类似)。
条件是 T 和 F其一,即条件永远不会是数字。
表达式的结果是数字 0-9, T 或者 F。

示例 1:

输入: “T?2:3”

输出: “2”

解释: 如果条件为真,结果为 2;否则,结果为 3。

示例 2:

输入: “F?1:T?4:5”

输出: “4”

解释: 条件表达式自右向左结合。使用括号的话,相当于:

         "(F ? 1 : (T ? 4 : 5))"                   "(F ? 1 : (T ? 4 : 5))"
      -> "(F ? 1 : 4)"                 或者     -> "(T ? 4 : 5)"
      -> "4"                                    -> "4"

示例 3:

输入: “T?T?F:5:3”

输出: “F”

解释: 条件表达式自右向左结合。使用括号的话,相当于:

         "(T ? (T ? F : 5) : 3)"                   "(T ? (T ? F : 5) : 3)"
      -> "(T ? F : 3)"                 或者       -> "(T ? F : 5)"
      -> "F"                                     -> "F"

链接:https://leetcode-cn.com/problems/ternary-expression-parser



删除注释

给一个 C++ 程序,删除程序中的注释。这个程序source是一个数组,其中source[i]表示第i行源码。 这表示每行源码由\n分隔。

在 C++ 中有两种注释风格,行内注释和块注释。

字符串// 表示行注释,表示//和其右侧的其余字符应该被忽略。

字符串/* 表示一个块注释,它表示直到*/的下一个(非重叠)出现的所有字符都应该被忽略。(阅读顺序为从左到右)非重叠是指,字符串/*/并没有结束块注释,因为注释的结尾与开头相重叠。

第一个有效注释优先于其他注释:如果字符串//出现在块注释中会被忽略。 同样,如果字符串/*出现在行或块注释中也会被忽略。

如果一行在删除注释之后变为空字符串,那么不要输出该行。即,答案列表中的每个字符串都是非空的。

样例中没有控制字符,单引号或双引号字符。比如,source = “string s = “/* Not a comment. */”;” 不会出现在测试样例里。(此外,没有其他内容(如定义或宏)会干扰注释。)

我们保证每一个块注释最终都会被闭合, 所以在行或块注释之外的/*总是开始新的注释。

最后,隐式换行符可以通过块注释删除。 有关详细信息,请参阅下面的示例。

从源代码中删除注释后,需要以相同的格式返回源代码。

示例 1:

输入:
source = ["/*Test program /", “int main()”, "{ ", " // variable declaration ", “int a, b, c;”, "/ This is a test", " multiline ", " comment for “, " testing */”, “a = b + c;”, “}”]

示例代码可以编排成这样:
/*Test program /
int main()
{
// variable declaration
int a, b, c;
/
This is a test
multiline
comment for
testing */
a = b + c;
}

输出: [“int main()”,"{ “,” “,“int a, b, c;”,“a = b + c;”,”}"]

编排后:
int main()
{

int a, b, c;
a = b + c;
}

解释:
第 1 行和第 6-9 行的字符串 /* 表示块注释。第 4 行的字符串 // 表示行注释。
示例 2:

输入:
source = [“a/comment", “line”, "more_comment/b”]
输出: [“ab”]
解释: 原始的 source 字符串是 “a/comment\nline\nmore_comment/b”, 其中我们用粗体显示了换行符。删除注释后,隐含的换行符被删除,留下字符串 “ab” 用换行符分隔成数组时就是 [“ab”].
注意:

source的长度范围为[1, 100].
source[i]的长度范围为[0, 80].
每个块注释都会被闭合。
给定的源码中不会有单引号、双引号或其他控制字符。

链接:https://leetcode-cn.com/problems/remove-comments

public IList<string> RemoveComments(string[] source) {
    var inBlock = false;
    var newline = new StringBuilder();
    var ans = new List<String>();
    foreach (string line in source) {
        int i = 0;
        char[] chars = line.ToCharArray();
        if (!inBlock) newline = new StringBuilder();
        while (i < line.Length) {
            if (!inBlock && i+1 < line.Length && chars[i] == '/' && chars[i+1] == '*') {
                inBlock = true;
                i++;
            } else if (inBlock && i+1 < line.Length && chars[i] == '*' && chars[i+1] == '/') {
                inBlock = false;
                i++;
            } else if (!inBlock && i+1 < line.Length && chars[i] == '/' && chars[i+1] == '/') {
                break;
            } else if (!inBlock) {
                newline.Append(chars[i]);
            }
            i++;
        }
        if (!inBlock && newline.Length > 0) {
            ans.Add(newline.ToString());
        }
    }
    return ans;
}

迷你语法分析器

给定一个用字符串表示的整数的嵌套列表,实现一个解析它的语法分析器。

列表中的每个元素只可能是整数或整数嵌套列表

提示:你可以假定这些字符串都是格式良好的:

字符串非空
字符串不包含空格
字符串只包含数字0-9, [, - , ]

示例 1:

给定 s = “324”,

你应该返回一个 NestedInteger 对象,其中只包含整数值 324。

示例 2:

给定 s = “[123,[456,[789]]]”,

返回一个 NestedInteger 对象包含一个有两个元素的嵌套列表:

  1. 一个 integer 包含值 123
  2. 一个包含两个元素的嵌套列表:
    i. 一个 integer 包含值 456
    ii. 一个包含一个元素的嵌套列表
    a. 一个 integer 包含值 789

链接:https://leetcode-cn.com/problems/mini-parser



Lisp 语法解析

给定一个类似 Lisp 语句的表达式 expression,求出其计算结果。

表达式语法如下所示:

表达式可以为整数,let 语法,add 语法,mult 语法。表达式的结果总是一个整数。
(整数可以是正整数、负整数、0)
let 语法表示为 (let v1 e1 v2 e2 … vn en expr), 其中 let语法总是以字符串 "let"来表示,接下来会跟随一个或多个交替变量或表达式,也就是说,第一个变量 v1被分配为表达式 e1 的值,第二个变量 v2 被分配为表达式 e2 的值,以此类推;最终 let 语法的值为 expr表达式的值。
add语法表示为 (add e1 e2),其中 add 语法总是以字符串 "add"来表示,该语法总是有两个表达式e1、e2, 该语法的最终结果是 e1 表达式的值与 e2 表达式的值之和。
mult语法表示为 (mult e1 e2) ,其中 mult 语法总是以字符串"mult"表示, 该语法总是有两个表达式 e1、e2,该语法的最终结果是 e1 表达式的值与 e2 表达式的值之积。
在该题目中,变量的命名以小写字符开始,之后跟随0个或多个小写字符或数字。为了方便,“add”,“let”,“mult"会被定义为"关键字”,不会在表达式的变量命名中出现。
最后,要说一下范围的概念。在做计算时,需要注意优先级,在最内层(根据括号)的表达式的值应该先计算,然后依次计算外层的表达式。我们将保证每一个测试的表达式都是合法的。有关范围的更多详细信息,请参阅示例。

示例:

输入: (add 1 2)
输出: 3

输入: (mult 3 (add 2 3))
输出: 15

输入: (let x 2 (mult x 5))
输出: 10

输入: (let x 2 (mult x (let x 3 y 4 (add x y))))
输出: 14
解释:
表达式 (add x y), 在获取 x 值时, 我们应当由最内层依次向外计算, 首先遇到了 x=3, 所以此处的 x 值是 3.

输入: (let x 3 x 2 x)
输出: 2
解释: let 语句中的赋值运算按顺序处理即可

输入: (let x 1 y 2 x (add x y) (add x y))
输出: 5
解释:
第一个 (add x y) 计算结果是 3,并且将此值赋给了 x 。
第二个 (add x y) 计算结果就是 3+2 = 5 。

输入: (let x 2 (add (let x 3 (let x 4 x)) x))
输出: 6
解释:
(let x 4 x) 中的 x 的作用范围仅在()之内。所以最终做加法操作时,x 的值是 2 。

输入: (let a1 3 b2 (add a1 1) b2)
输出: 4
解释:
变量命名时可以在第一个小写字母后跟随数字.

注意:

我们给定的 expression 表达式都是格式化后的:表达式前后没有多余的空格,表达式的不同部分(关键字、变量、表达式)之间仅使用一个空格分割,并且在相邻括号之间也没有空格。我们给定的表达式均为合法的且最终结果为整数。
我们给定的表达式长度最多为 2000 (表达式也不会为空,因为那不是一个合法的表达式)。
最终的结果和中间的计算结果都将是一个 32 位整数。

链接:https://leetcode-cn.com/problems/parse-lisp-expression



原子的数量

给定一个化学式formula(作为字符串),返回每种原子的数量。

原子总是以一个大写字母开始,接着跟随0个或任意个小写字母,表示原子的名字。

如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。例如,H2O 和 H2O2 是可行的,但 H1O2 这个表达是不可行的。

两个化学式连在一起是新的化学式。例如 H2O2He3Mg4 也是化学式。

一个括号中的化学式和数字(可选择性添加)也是化学式。例如 (H2O2) 和 (H2O2)3 是化学式。

给定一个化学式,输出所有原子的数量。格式为:第一个(按字典序)原子的名子,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。

示例 1:

输入:
formula = “H2O”
输出: “H2O”
解释:
原子的数量是 {‘H’: 2, ‘O’: 1}。
示例 2:

输入:
formula = “Mg(OH)2”
输出: “H2MgO2”
解释:
原子的数量是 {‘H’: 2, ‘Mg’: 1, ‘O’: 2}。
示例 3:

输入:
formula = “K4(ON(SO3)2)2”
输出: “K4N2O14S4”
解释:
原子的数量是 {‘K’: 4, ‘N’: 2, ‘O’: 14, ‘S’: 4}。
注意:

所有原子的第一个字母为大写,剩余字母都是小写。
formula的长度在[1, 1000]之间。
formula只包含字母、数字和圆括号,并且题目中给定的是合法的化学式。

链接:https://leetcode-cn.com/problems/number-of-atoms



你可能感兴趣的:(基础算法)