如果你熟悉 Shell 编程,那么一定了解过花括号展开,它可以用来生成任意字符串。

花括号展开的表达式可以看作一个由 花括号逗号小写英文字母 组成的字符串,定义下面几条语法规则:

  • 如果只给出单一的元素x,那么表达式表示的字符串就只有"x"

    • 例如,表达式 {a} 表示字符串 "a"
    • 而表达式 {ab} 就表示字符串 "ab"
  • 当两个或多个表达式并列,以逗号分隔时,我们取这些表达式中元素的并集。
    • 例如,表达式 {a,b,c} 表示字符串 "a","b","c"
    • 而表达式 {a,b},{b,c} 也可以表示字符串 "a","b","c"
  • 要是两个或多个表达式相接,中间没有隔开时,我们从这些表达式中各取一个元素依次连接形成字符串。
    • 例如,表达式 {a,b}{c,d} 表示字符串 "ac","ad","bc","bd"
  • 表达式之间允许嵌套,单一元素与表达式的连接也是允许的。
    • 例如,表达式 a{b,c,d} 表示字符串 "ab","ac","ad"
    • 例如,表达式 {a{b,c}}{{d,e}f{g,h}} 可以代换为 {ab,ac}{dfg,dfh,efg,efh},表示字符串 "abdfg", "abdfh", "abefg", "abefh", "acdfg", "acdfh", "acefg", "acefh"。

给出表示基于给定语法规则的表达式 expression,返回它所表示的所有字符串组成的有序列表。

假如你希望以「集合」的概念了解此题,也可以通过点击 “显示英文描述” 获取详情。

示例 1:

输入:"{a,b}{c{d,e}}"
输出:["acd","ace","bcd","bce"]

示例 2:

输入:"{{a,z}, a{b,c}, {ab,z}}"
输出:["a","ab","ac","z"]
解释:输出中 不应 出现重复的组合结果。

提示:

  1. 1 <= expression.length <= 50
  2. expression[i]'{''}'',' 或小写英文字母组成
  3. 给出的表达式 expression 用以表示一组基于题目描述中语法构造的字符串

解题思路

实际上这个问题和字节跳动2019笔试:字符串展开(超详细的解法!!!)有点类似,不过在这个问题比之前问题的难度更大,但是解题思路是一样的。

对于这种情况复杂的问题,我们不妨将其拆解为几个子问题处理。例如这题,我们需要实现

  • {a,b,c}
  • {a,b},{b,c}
  • {a,b}{c,d}
  • a{b,c,d}
  • {a{b,c}}{{d,e}f{g,h}}

第一个的实现非常简单

def f1(self, s1):return list(set(s1[1:-1].split(",")))

第二个功能的实现只需调用第一个即可。

def f2(self, s2):i = s2.find("}")+1l = self.f1(s2[:i])r = self.f1(s2[i+1:])return list(set(l + r))

第三个功能的实现也需要调用第一个,然后计算笛卡尔积

def f3(self, s3):i = s3.find("}")+1l = self.f1(s3[:i])r = self.f1(s3[i:])return list("".join(s) for s in itertools.product(l, r))

第四个功能和第三个类似

def f4(self, s4):i = s3.find("{")l = s3[:i]r = self.f1(s3[i:])return list("".join(s) for s in itertools.product(l, r))

好,此时我们来处理递归结构,但是我们发现很难在一个递归结构中去判断上述的字符串。怎么办?说明我们问题切分的还不够细。实际上这个问题最本质在于是不是有逗号?如果有的话,我们左右元素做并集,否则做笛卡尔积,并且还有一点要注意的是笛卡尔积的优先级高。那么我们需要对上面的函数做一些修改。

我们先写两个基本操作函数。

def un(self, s):return set().union(*s)
def pr(self, s):return set("".join(i) for i in itertools.product(*s))

现在我们有了这两个工具那就好办了。接下来的问题就是处理递归,我们假设处理函数是 f f f的话,那么

  • f ( x , y , z ) = f ( x ) ∪ f ( y ) ∪ f ( z ) f(x,y,z)=f(x)\cup f(y)\cup f(z) f(x,y,z)=f(x)∪f(y)∪f(z)
  • f ( x y z ) = f ( x ) × f ( y ) × f ( z ) f(xyz)=f(x)\times f(y)\times f(z) f(xyz)=f(x)×f(y)×f(z)

我们定义函数 f 1 f_1 f1​处理第一个情况, f 2 f_2 f2​处理第二种情况。由于笛卡尔积的优先级高,所以我们在调用 f 1 f_1 f1​函数前,需要先调用 f 2 f_2 f2​,那么上面两个式子就可以转化为

  • f 1 ( x , y , z ) = f 2 ( x ) ∪ f 2 ( y ) ∪ f 2 ( z ) f_1(x,y,z)=f_2(x)\cup f_2(y)\cup f_2(z) f1​(x,y,z)=f2​(x)∪f2​(y)∪f2​(z)
  • f 2 ( x y z ) = f 1 ( x ) × f 1 ( y ) × f 1 ( z ) f_2(xyz)=f_1(x)\times f_1(y)\times f_1(z) f2​(xyz)=f1​(x)×f1​(y)×f1​(z)

那么下面的代码就顺理成章了

import itertools
class Solution:def braceExpansionII(self, expression):self.u, self.n = 0, len(expression)return sorted(list(self.f2(expression)))def f1(self, s1):res = list()while self.u < self.n:if s1[self.u] == '}':self.u += 1breakif s1[self.u] == ',':self.u += 1continuer = self.f2(s1)res.append(r)return self.un(res)def f2(self, s2):res = list()while self.u < self.n:if s2[self.u] == '{':self.u += 1r = self.f1(s2)res.append(r)elif s2[self.u].isalpha():ts = ""while self.u < self.n and s2[self.u].isalpha():ts += s2[self.u]self.u += 1res.append({ts})else:breakreturn self.pr(res)def un(self, s):return set().union(*s)def pr(self, s):return set("".join(i) for i in itertools.product(*s))

还有一个比较清晰的思路就是对每个"{}"对分层考虑,对于每一层我们先处理笛卡尔积然后处理并集操作。我们可以通过变量level记录括号的层级信息,当我们碰到"{"的时候,我们的level++;当我们碰到"}",我们的level--,当level==0的时候就表示我们此时的一层遍历结束,我们需要递归处理这一层里面的表达式。例如

{ a , b } { c { d , e } }l             r

此时exp[l:r]就是属于同一层级的表达式,我们需要递归处理exp[l:r]里面的内容。

level==0并且遍历到的元素是","的话,说明我们需要处理一个新的表达式了(并且这个表达式的层级和之前处理的是一致的);如果level==0,但是遍历到的元素是字母的话,我们只需将字母添加到上一个表达式末尾即可。那么边界条件是什么呢?

边界条件就是要处理的字符串是空的时候,这个时候返回一个空的列表即可。

import itertools
class Solution:def braceExpansionII(self, expression):groups = [[]]level = 0for i, c in enumerate(expression):if c == '{':if level == 0:start = i+1level += 1elif c == '}':level -= 1if level == 0:groups[-1].append(self.braceExpansionII(expression[start:i]))elif level == 0:if c == ",":groups.append([])else:groups[-1].append([c])return sorted(set().union(*[set(map(''.join, itertools.product(*group))) for group in groups]))

代码非常的简洁。

reference:

https://leetcode.com/problems/brace-expansion-ii/discuss/317732/ChineseC%2B%2B-1096.

https://leetcode.com/problems/brace-expansion-ii/discuss/318744/Python3-solution-after-analyzing-the-grammar-of-the-expression

https://leetcode.com/problems/brace-expansion-ii/discuss/317623/Python3-Clear-and-Short-Recursive-Solution

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

Leetcode 1096:花括号展开 II(超详细!!!)相关推荐

  1. shell 展开之花括号展开

    花括号展开的形式 花括号展开看如下一个例子 $ echo a{b,c,d}e abe ace ade a{b,c,d}e 是花括号展开的一种形式,其中 a 表示前缀,e 表示后缀,{b,c,d} 依次 ...

  2. 1087 花括号展开

    题目描述: 我们用一个特殊的字符串 S 来表示一份单词列表,之所以能展开成为一个列表,是因为这个字符串 S 中存在一个叫做「选项」的概念: 单词中的每个字母可能只有一个选项或存在多个备选项.如果只有一 ...

  3. Leetcode 37:解数独(超详细的解法!!!)

    编写一个程序,通过已填充的空格来解决数独问题. 一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出现一次. 数字 1-9 在每一个以粗实线分隔的 3x3 ...

  4. Leetcode 739:每日温度(超详细的解法!!!)

    根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高的天数.如果之后都不会升高,请输入 0 来代替. 例如,给定一个列表 temperatures = [73, 74 ...

  5. Leetcode 1134:阿姆斯特朗数(超详细的解法!!!)

    假设存在一个 k 位数 N,其每一位上的数字的 k 次幂的总和也是 N,那么这个数是阿姆斯特朗数. 给你一个正整数 N,让你来判定他是否是阿姆斯特朗数,是则返回 true,不是则返回 false. 示 ...

  6. Leetcode 1419:数青蛙(超详细的解法!!!)

    给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 "croak" )的组合.由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrogs 中会混合 ...

  7. Leetcode 1197:进击的骑士(超详细的解法!!!)

    一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上,你的 骑士 驻扎在坐标为 [0, 0] 的方格里. 骑士的走法和中国象棋中的马相似,走 "日" ...

  8. Leetcode 1231:分享巧克力(超详细的解法!!!)

    你有一大块巧克力,它由一些甜度不完全相同的小块组成.我们用数组 sweetness 来表示每一小块的甜度. 你打算和 K 名朋友一起分享这块巧克力,所以你需要将切割 K 次才能得到 K+1 块,每一块 ...

  9. Leetcode 1263:推箱子(超详细的解法!!!)

    「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置. 游戏地图用大小为 n * m 的网格 grid 表示,其中每个元素可以是墙.地板或者是箱子. 现在你将作为玩家参与游戏,按规 ...

最新文章

  1. 字符串截取固定长度的方法
  2. [转]web标准的几个误区
  3. vs2010打不开vs2017的.sln文件,出现错误提示 “选择的文件是解决方案文件 但是用此应用程序的较新版本创建的,无法打开”
  4. 一个python程序员需要掌握的知识-Python基础知识,初学者的你掌握了吗?
  5. 设计模式:模板方法(Template Method Pattern)
  6. JZOJ 5253. 排列与交换
  7. Fragment使用LocalBroadcastManager接收广播消息
  8. 赞!Google 资深软件工程师 LeetCode 刷题笔记首次公开
  9. docker 网络模型
  10. Cisco Catalyst 4500
  11. MySQL 隐式转换 字符串和整型说明
  12. sql 动态写入数据库字段_批处理写入,动态SQL和参数化SQL,数据库的性能如何?...
  13. JLBH示例3 –吞吐量对延迟的影响
  14. sql server 自定义函数
  15. Spring MVC核心知识
  16. [USACO4.2] 草地排水 Drainage Ditches (最大流)
  17. 《LabVIEW FPGA开发宝典》第9章:利用树莓派Linux RT+FPGA PCIe实现国产化RIO
  18. python标准库os中用来列出_Python标准库os中的listdir()函数用来列出指定目录中
  19. H2---主题:融入动画技术的交互应用
  20. mysql 锁级别说明 一

热门文章

  1. 《图解密码技术》分组密码(4) 密文反馈CFB模式
  2. 西雅图不相信爱情,66岁比尔盖茨离婚!财产分割成焦点
  3. Python 模拟浏览器 POST请求思路
  4. 使用ireport设计jasperreport报表模板,以及简单的设计使用案例
  5. django后台返回excel表
  6. 【万里征程——Windows App开发】画笔和图像
  7. Google Earth Engine(GEE)——1870年至2100年的全球城市范围数据集
  8. TS——总结函数类型的定义方法
  9. 微生物组-宏基因组分析(线上开课,2021.1)
  10. nova4Android版本,华为nova 4和nova 4e有什么区别不同?配置参数完整对比