You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

布尔表达式括号排列求值计数优化:求高效算法技巧

优化方案:动态规划替代枚举括号排列

你的核心问题在于当前算法枚举所有可能的括号组合(卡特兰数级别的指数复杂度),这对于较长输入必然超时。改用**动态规划(DP)**可以将时间复杂度降至O(n³),大幅减少计算量。

DP思路详解

定义两个二维数组:

  • dp[i][j][0]:计算从第i个到第j个布尔值组成的子表达式,结果为false的合法括号排列数量
  • dp[i][j][1]:结果为true的数量

基础情况

当子表达式只有一个布尔值时(i == j):

  • 若s[i]为't',则dp[i][i][1] = 1,dp[i][i][0] = 0
  • 若s[i]为'f',则dp[i][i][0] = 1,dp[i][i][1] = 0

状态转移

对于每个长度大于1的子表达式i~j,遍历所有可能的分割点k(i ≤ k < j),对应运算符为ops[k]。根据运算符的逻辑,结合左右子表达式的true/false组合数:

  • &运算符:结果为true仅当左右都为true;其余情况为false
  • |运算符:结果为false仅当左右都为false;其余情况为true
  • ^运算符:结果为true仅当左右结果不同;相同则为false

优化后的C++代码

#include <vector>
#include <string>
#include <iostream>

using namespace std;

int64_t solve(const string &s, const string &ops) {
    int n = s.size();
    if (n < 2 || ops.size() != n - 1) return 0;

    // dp[i][j][0]: 子表达式i~j结果为false的数量
    // dp[i][j][1]: 子表达式i~j结果为true的数量
    vector<vector<vector<int64_t>>> dp(n, vector<vector<int64_t>>(n, vector<int64_t>(2, 0)));

    // 初始化基础情况
    for (int i = 0; i < n; ++i) {
        if (s[i] == 't') {
            dp[i][i][1] = 1;
            dp[i][i][0] = 0;
        } else {
            dp[i][i][0] = 1;
            dp[i][i][1] = 0;
        }
    }

    // 遍历子表达式长度,从2到n
    for (int len = 2; len <= n; ++len) {
        for (int i = 0; i + len <= n; ++i) {
            int j = i + len - 1;
            dp[i][j][0] = 0;
            dp[i][j][1] = 0;

            // 遍历所有分割点k
            for (int k = i; k < j; ++k) {
                char op = ops[k];
                int64_t left0 = dp[i][k][0], left1 = dp[i][k][1];
                int64_t right0 = dp[k+1][j][0], right1 = dp[k+1][j][1];

                switch(op) {
                    case '&':
                        dp[i][j][1] += left1 * right1;
                        dp[i][j][0] += left0 * right0 + left0 * right1 + left1 * right0;
                        break;
                    case '|':
                        dp[i][j][1] += left1 * right1 + left1 * right0 + left0 * right1;
                        dp[i][j][0] += left0 * right0;
                        break;
                    case '^':
                        dp[i][j][1] += left1 * right0 + left0 * right1;
                        dp[i][j][0] += left1 * right1 + left0 * right0;
                        break;
                }
            }
        }
    }

    return dp[0][n-1][1];
}

int main(int argc, char **argv) {
    cout << solve("ttftfftftf", "|&^&&||^&") << endl;
    return 0;
}

为什么这比原算法好

  • 时间复杂度:原算法是卡特兰数级别的指数复杂度(O(4ⁿ/n^(3/2))),而DP是O(n³),对于n=20的输入,前者需要数百万次计算,后者仅8000次。
  • 避免冗余:原算法中存储括号模式字符串的开销很大,且很多重复的子表达式会被多次计算,DP则直接缓存每个子表达式的结果,无需重复计算。
  • 逻辑简洁:DP直接计算所有可能的组合数,不需要提前终止分支,代码更易维护。

内容的提问来源于stack exchange,提问作者John Ziegler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:04:56