表达式得到期望结果的组成种数问题

表达式,得到,期望,结果,组成,种数,问题 · 浏览次数 : 243

小编点评

该算法的生成过程需要带简单的排版,主要包括以下几个方面: 1. **数据结构选择**: 由于需要进行大量的计算,所以选择二维数组 **tMap** 和 **fMap** 来存储数据。 2. **数据初始化**: 由于需要对数据进行初始化,所以需要在 **tMap** 和 **fMap** 中进行数据初始化。 3. **计算规则**: 为了计算出最终结果,需要进行各种计算,这些计算规则需要在代码中进行实现。 4. **排版与数据结构选择**: 由于需要对数据进行排版,所以需要在代码中进行排版。 此外,还需要考虑以下一些细节: 1. **错误处理**: 在代码中需要进行错误处理,例如当数据格式不正确或计算规则出错等,需要进行对应处理。 2. **排版效率**: 为了提升排版效率,可以考虑将一些计算规则进行优化,例如使用缓存等技术。 3. **算法复杂度**: 为了优化算法复杂度,可以考虑使用一些数据结构优化等技术。 总结,该算法的生成过程需要带简单的排版,主要包括数据结构选择、数据初始化、计算规则、排版与数据结构选择以及错误处理等方面。

正文

表达式得到期望结果的组成种数问题

作者:Grey

原文地址:

博客园:表达式得到期望结果的组成种数问题

CSDN:表达式得到期望结果的组成种数问题

题目描述

给定一个只由 0(假)、1(真)、&(逻辑与)、|(逻辑或)、^(异或)五种字符组成的字符串 exp,再给定一个布尔值 desired。返回 exp 能有多少种组合方式,可以达到 desired 的结果。

例如:

exp ="1^0|0|1",desired = false 只有 1^((0|0)|1)1^(0|(0|1)) 的组合可以得到 false,返回 2;

exp ="1",desired = false,无组合可以得到 false,返回0。

题目链接见:牛客-表达式得到期望结果的组成种数

暴力解法

首先,我们可以做一次初步过滤,初步判断下 exp 的合法性,代码和注释如下:

    // 初步筛选一下exp串的合法性
    public static boolean errorFormat(char[] exp, int n) {
        if ((n & 1) == 0) {
            // 表达式不能为偶数个长度
            return true;
        }
        for (int i = 0; i < n; i += 2) {
            if (exp[i] != '1' && exp[i] != '0') {
                // 0,2,4,8...n-1位置上一定只能是 1 或者 0
                return true;
            }
        }
        for (int i = 1; i < n; i += 2) {
            if (exp[i] != '|' && exp[i] != '^' && exp[i] != '&') {
                return true;
            }
        }
        return false;
    }

定义递归函数

int p(char[] exp, int L, int R, boolean desired)

递归含义表示:exp 这个字符串,从 L 到 R 区间内,可以得到 desired 结果的组合数量是多少。

首先考虑 base case,即:只有一个字符的时候,此时 L == R

有如下三种情况:

if (L == R) {
    // 只有一个字符的时候,
    if (desired && exp[L] == '1') {
        return 1;
    } else if (!desired && exp[L] == '0') {
        return 1;
    } else {
        return 0;
    }
}     

接下来是普遍情况,分别枚举每个操作符可能在的位置的左右两侧的组合数量,然后做乘积即可,代码如下

        for (int i = L + 1; i < R; i++) {
            if (exp[i] == '&') {
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                }
            } else if (exp[i] == '|') {
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                }
            } else {
                // exp[i] == '^'
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                }
            }
        }

暴力解法的完整代码如下:

    public static int getDesiredNum(String exp, boolean desired) {
        char[] str = exp.toCharArray();
        int N = str.length;
        if (errorFormat(str, N)) {
            return 0;
        }
        return p(str, 0, N - 1, desired);
    }

    // 初步筛选一下exp串的合法性
    public static boolean errorFormat(char[] exp, int n) {
        if ((n & 1) == 0) {
            // 表达式不能为偶数个长度
            return true;
        }
        for (int i = 0; i < n; i += 2) {
            if (exp[i] != '1' && exp[i] != '0') {
                // 0,2,4,8...n-1位置上一定只能是 1 或者 0
                return true;
            }
        }
        for (int i = 1; i < n; i += 2) {
            if (exp[i] != '|' && exp[i] != '^' && exp[i] != '&') {
                return true;
            }
        }
        return false;
    }

    public static int p(char[] exp, int L, int R, boolean desired) {
        if (L == R) {
            if (desired && exp[L] == '1') {
                return 1;
            } else if (!desired && exp[L] == '0') {
                return 1;
            } else {
                return 0;
            }
        }
        int res = 0;

        for (int i = L + 1; i < R; i++) {
            if (exp[i] == '&') {
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                }
            } else if (exp[i] == '|') {
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                }
            } else {
                // exp[i] == '^'
                if (desired) {
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, true);
                } else {
                    res += p(exp, L, i - 1, false) * p(exp, i + 1, R, false);
                    res += p(exp, L, i - 1, true) * p(exp, i + 1, R, true);
                }
            }
        }
        return res;
    }

本题中,使用暴力递归解法已经可以 AC。

动态规划解法

上述暴力递归方法中,有三个可变参数 L , R 和 desired,我们可以定义两个二维数组

int[][] tMap = new int[N][N];
int[][] fMap = new int[N][N];

其中

tMap[i][j]表示 i 到 j 能组成 true 的数量是多少,即暴力递归中的p(exp,i,j,true)

fMap[i][j]表示 i 到 j 能组成 false 的数量是多少,即暴力递归中的p(exp,i,j,false)

这个二维数组的对角线下半区无用。

tMap[i][j]fMap[i][j] 的转移方程可以根据暴力递归方法来实现,完整代码如下:

    public static int getDesiredNum(String exp, boolean desired) {
        char[] str = exp.toCharArray();
        int N = str.length;
        if (errorFormat(str, N)) {
            return 0;
        }
        //tMap[i][j] 表示i到j能组成true的数量是多少,所以对角线下半区无用
        int[][] tMap = new int[N][N];
        //fMap[i][j] 表示i到j能组成false的数量是多少,所以对角线下半区无用
        int[][] fMap = new int[N][N];

        for (int i = 0; i < N; i += 2) {
            // 忽视符号位
            tMap[i][i] = str[i] == '1' ? 1 : 0;
            fMap[i][i] = str[i] == '0' ? 1 : 0;
        }
        for (int L = N - 3; L >= 0; L -= 2) {
            for (int R = L + 2; R < N; R += 2) {
                for (int i = L + 1; i < R; i += 2) {
                    if (str[i] == '&') {
                        tMap[L][R] += tMap[L][i - 1] * tMap[i + 1][R];
                        fMap[L][R] += tMap[L][i - 1] * fMap[i + 1][R];
                        fMap[L][R] += fMap[L][i - 1] * fMap[i + 1][R];
                        fMap[L][R] += fMap[L][i - 1] * tMap[i + 1][R];
                    } else if (str[i] == '|') {
                        tMap[L][R] += tMap[L][i - 1] * fMap[i + 1][R];
                        tMap[L][R] += tMap[L][i - 1] * tMap[i + 1][R];
                        tMap[L][R] += fMap[L][i - 1] * tMap[i + 1][R];
                        fMap[L][R] += fMap[L][i - 1] * fMap[i + 1][R];
                    } else {
                        tMap[L][R] += tMap[L][i - 1] * fMap[i + 1][R];
                        tMap[L][R] += fMap[L][i - 1] * tMap[i + 1][R];
                        fMap[L][R] += fMap[L][i - 1] * fMap[i + 1][R];
                        fMap[L][R] += tMap[L][i - 1] * tMap[i + 1][R];
                    }
                }
            }
        }
        return desired ? tMap[0][N - 1] : fMap[0][N - 1];
    }
    public static boolean errorFormat(char[] exp, int n) {
        if ((n & 1) == 0) {
            // 表达式不能为偶数个长度
            return true;
        }
        for (int i = 0; i < n; i += 2) {
            if (exp[i] != '1' && exp[i] != '0') {
                // 0,2,4,8...n-1位置上一定只能是 1 或者 0
                return true;
            }
        }
        for (int i = 1; i < n; i += 2) {
            if (exp[i] != '|' && exp[i] != '^' && exp[i] != '&') {
                return true;
            }
        }
        return false;
    }

更多

算法和数据结构笔记

与表达式得到期望结果的组成种数问题相似的内容:

表达式得到期望结果的组成种数问题

表达式得到期望结果的组成种数问题 作者:Grey 原文地址: 博客园:表达式得到期望结果的组成种数问题 CSDN:表达式得到期望结果的组成种数问题 题目描述 给定一个只由 0(假)、1(真)、&(逻辑与)、|(逻辑或)、^(异或)五种字符组成的字符串 exp,再给定一个布尔值 desired。返回

CF98C Help Greg the Dwarf 题解

CF98C Help Greg the Dwarf 题解 为什么不三分? 首先我们考虑如何求出答案。 如图,考虑设夹角为 \(\theta\),那么可以得到表达式: \[[\cfrac a {\tan \theta} - (l \cos \theta - b)] \sin \theta \]整理可得

#Powerbi 10分钟,理解 Rankx 排名函数

一:本文思维导图及示例数据图 1.1思维导图 1.2 示例数据图 二:度量值示例 2.1 函数简介 RANKX 首先为的每一行计值表达式,将结果临时存储为一个值列表。然后在当前筛选上下文中计值,将得到的结果与列表中的值进行比较,根据排名规则和的设置,返回最终排名。 2.2 产品排名(稠密)度量值 这

[转帖]并发delete导致oracle死锁问题的解决

项目中有一个批处理任务,用来删除数据库中过期的数据(包括说话人的语音、模型、记录等),当程序被分布式部署后,就会有多个批处理线程同时进行删除,不过不同的线程,会根据元信息表得到不同的说话人信息,从而删除不同的数据,并不存在竞争的问题,但是,当项目使用oracle数据库在线上运行时,却频繁出现了ORA

TiDB恢复部分表的方式方法

TiDB恢复部分表的方式方法 背景 今天同事告知误删了部分表. 因为是UAT准生产的环境, 所以仅有每天晚上11点的备份处理. 同时告知 昨天的数据也可以. 得到认可后进行了 TiDB的单表备份恢复. 备份的语句 注意TiDB是可以增量备份恢复的 但是为了快速的恢复和解决背景中的问题. 我这边采用保

Troubleshooting 专题 - 问正确的问题 得到正确的答案

在很多公司中,IT、数据中心、业务系统一出故障,会有很多人被叫到作战室(就是一个为了解决该问题,而把所有相关人员集中在一起的一个会议室), 但是对于这个问题他们是否可以修复, 是否他们应该负有责任, 经常没有线索. 「证据」(基础架构监控数据, 日志文件, 用户投诉等等) 表明了症状, 但是与 ro

【pandas小技巧】--数据转置

所谓**数据转置**,就是是将原始数据表格沿着对角线翻折,使原来的行变成新的列,原来的列变成新的行,从而更方便地进行数据分析和处理。 `pandas`中`DataFrame`的转置非常简单,每个`DataFrame`对象都有一个`T`属性,通过这个属性就能得到转置之后的`DataFrame`。下面介

跨域推荐:嵌入映射、联合训练和解耦表征

跨域推荐旨在利用从其它相关源域收集的用户-物品交互信息以提升目标域的推荐质量。传统的跨域推荐方法常常基于嵌入和映射(Embedding and Mapping,EMCDR) 的思路,这种方法在进行对齐操作之前,各领域需要先通过预训练以独立地得到用户/物品的embeddings。因此,有偏的(biased) 预训练表征将无可避免地包含领域特有的(domain-specific) 信息,从而会导致对跨

图数据挖掘:网络的常见度量属性

网络的度分布p(k)表示了一个随机选择的节点拥有度k的概率。我们设度为k的节点数目Nk =#nodes with degree k,除以节点数量N则可得到归一化后的概率质量分布 p(k) = Nk/N。图的路径(path)指一个节点序列,使得序列中的每个节点都链接到序列中的下一个节点,一个路径可以通过经过同一条边多次而和它自身相交。

Qt开发技术:Q3D图表开发笔记(四):Q3DSurface三维曲面图颜色样式详解、Demo以及代码详解

前言 qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的。 其中就包括华丽绚烂的三维图表,数据量不大的时候是可以使用的。 前面介绍了基础的q3d散点图、柱状图、三维曲面图,本片深入对三维曲面图支持的颜色表现方式进行探