小红的能量校准

题目分析

给定一个关于变量 的一元一次方程,形如 左侧表达式=右侧整数。左侧表达式包含变量 、非负整数、运算符 +*(),且存在隐式乘法——当数字、变量、括号直接相邻时,中间省略了乘号。例如 2(x+1) 表示 x3 表示

在左侧恰好出现一次,方程保证有整数解,求 的值。

思路

递归下降解析 + 线性多项式运算

由于 只出现一次,左侧表达式关于 是线性的,可以表示为 的形式。我们用一个二元组 来表示每个子表达式的值,其中 的系数, 是常数项。

定义运算规则:

  • 加法
  • 减法
  • 乘法(因为线性,

递归下降解析表达式,按优先级从低到高分三层:

  1. parseExpr:处理 +-,解析若干 term 的加减。
  2. parseTerm:处理 *隐式乘法。当下一个字符是 (x 或数字时,即为隐式乘法。
  3. parseFactor:处理最基本的单元——数字返回 ,变量 返回 ,括号则递归调用 parseExpr

解析完左侧得到 ,右侧为整数 ,则

代码

#include <bits/stdc++.h>
using namespace std;

typedef pair<long long, long long> P;
string s;
int pos;

P parseExpr();
P parseTerm();
P parseFactor();

P mul(P p1, P p2) {
    return {p1.first * p2.second + p2.first * p1.second, p1.second * p2.second};
}

P parseExpr() {
    P res = parseTerm();
    while (pos < (int)s.size() && (s[pos] == '+' || s[pos] == '-')) {
        char op = s[pos++];
        P t = parseTerm();
        if (op == '+') res = {res.first + t.first, res.second + t.second};
        else res = {res.first - t.first, res.second - t.second};
    }
    return res;
}

P parseTerm() {
    P res = parseFactor();
    while (pos < (int)s.size()) {
        if (s[pos] == '*') {
            pos++;
            res = mul(res, parseFactor());
        } else if (s[pos] == '(' || s[pos] == 'x' || isdigit(s[pos])) {
            res = mul(res, parseFactor());
        } else break;
    }
    return res;
}

P parseFactor() {
    if (s[pos] == '(') {
        pos++;
        P res = parseExpr();
        pos++;
        return res;
    } else if (s[pos] == 'x') {
        pos++;
        return {1, 0};
    } else {
        long long num = 0;
        while (pos < (int)s.size() && isdigit(s[pos]))
            num = num * 10 + (s[pos++] - '0');
        return {0, num};
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    getline(cin, s);
    int eq = s.find('=');
    long long rhs = stoll(s.substr(eq + 1));
    s = s.substr(0, eq);
    pos = 0;
    P lhs = parseExpr();
    cout << (rhs - lhs.second) / lhs.first << endl;
}
import java.util.Scanner;

public class Main {
    static String s;
    static int pos;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        int eq = line.indexOf('=');
        long rhsVal = Long.parseLong(line.substring(eq + 1));
        s = line.substring(0, eq);
        pos = 0;
        long[] lhs = parseExpr();
        System.out.println((rhsVal - lhs[1]) / lhs[0]);
    }

    static long[] parseExpr() {
        long[] res = parseTerm();
        while (pos < s.length() && (s.charAt(pos) == '+' || s.charAt(pos) == '-')) {
            char op = s.charAt(pos++);
            long[] t = parseTerm();
            if (op == '+') { res[0] += t[0]; res[1] += t[1]; }
            else { res[0] -= t[0]; res[1] -= t[1]; }
        }
        return res;
    }

    static long[] parseTerm() {
        long[] res = parseFactor();
        while (pos < s.length()) {
            if (s.charAt(pos) == '*') {
                pos++;
                long[] f = parseFactor();
                res = mul(res, f);
            } else if (s.charAt(pos) == '(' || s.charAt(pos) == 'x' || Character.isDigit(s.charAt(pos))) {
                res = mul(res, parseFactor());
            } else break;
        }
        return res;
    }

    static long[] parseFactor() {
        if (s.charAt(pos) == '(') {
            pos++;
            long[] res = parseExpr();
            pos++;
            return res;
        } else if (s.charAt(pos) == 'x') {
            pos++;
            return new long[]{1, 0};
        } else {
            long num = 0;
            while (pos < s.length() && Character.isDigit(s.charAt(pos)))
                num = num * 10 + (s.charAt(pos++) - '0');
            return new long[]{0, num};
        }
    }

    static long[] mul(long[] a, long[] b) {
        return new long[]{a[0] * b[1] + b[0] * a[1], a[1] * b[1]};
    }
}
import sys

def solve():
    line = input().strip()
    eq = line.index('=')
    lhs_str = line[:eq]
    rhs_val = int(line[eq+1:])

    s = lhs_str
    pos = [0]

    def parse_expr():
        a, b = parse_term()
        while pos[0] < len(s) and s[pos[0]] in '+-':
            op = s[pos[0]]; pos[0] += 1
            ta, tb = parse_term()
            if op == '+': a += ta; b += tb
            else: a -= ta; b -= tb
        return a, b

    def parse_term():
        a, b = parse_factor()
        while pos[0] < len(s):
            c = s[pos[0]]
            if c == '*':
                pos[0] += 1
                fa, fb = parse_factor()
                a, b = a * fb + fa * b, b * fb
            elif c == '(' or c == 'x' or c.isdigit():
                fa, fb = parse_factor()
                a, b = a * fb + fa * b, b * fb
            else: break
        return a, b

    def parse_factor():
        c = s[pos[0]]
        if c == '(':
            pos[0] += 1
            a, b = parse_expr()
            pos[0] += 1
            return a, b
        elif c == 'x':
            pos[0] += 1
            return 1, 0
        else:
            num = 0
            while pos[0] < len(s) and s[pos[0]].isdigit():
                num = num * 10 + int(s[pos[0]])
                pos[0] += 1
            return 0, num

    a, b = parse_expr()
    print((rhs_val - b) // a)

solve()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });

rl.on('line', (line) => {
    line = line.trim();
    const eq = line.indexOf('=');
    const lhs = line.substring(0, eq);
    const rhsVal = BigInt(line.substring(eq + 1));

    let pos = 0;
    const s = lhs;

    function parseExpr() {
        let [a, b] = parseTerm();
        while (pos < s.length && (s[pos] === '+' || s[pos] === '-')) {
            const op = s[pos++];
            const [ta, tb] = parseTerm();
            if (op === '+') { a += ta; b += tb; }
            else { a -= ta; b -= tb; }
        }
        return [a, b];
    }

    function parseTerm() {
        let [a, b] = parseFactor();
        while (pos < s.length) {
            if (s[pos] === '*') {
                pos++;
                const [fa, fb] = parseFactor();
                [a, b] = [a * fb + fa * b, b * fb];
            } else if (s[pos] === '(' || s[pos] === 'x' || (s[pos] >= '0' && s[pos] <= '9')) {
                const [fa, fb] = parseFactor();
                [a, b] = [a * fb + fa * b, b * fb];
            } else break;
        }
        return [a, b];
    }

    function parseFactor() {
        if (s[pos] === '(') {
            pos++;
            const res = parseExpr();
            pos++;
            return res;
        } else if (s[pos] === 'x') {
            pos++;
            return [1n, 0n];
        } else {
            let num = 0n;
            while (pos < s.length && s[pos] >= '0' && s[pos] <= '9')
                num = num * 10n + BigInt(s[pos++].charCodeAt(0) - 48);
            return [0n, num];
        }
    }

    const [a, b] = parseExpr();
    console.log(((rhsVal - b) / a).toString());
    rl.close();
});

复杂度分析

  • 时间复杂度,其中 为表达式字符串长度。每个字符恰好被访问一次。
  • 空间复杂度,其中 为括号嵌套深度,即递归栈的深度。