赞
踩
作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例
python源码解读
程序员必备的数学知识与应用
备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级
给定三个字符串 s1
, s2
, 和 s3
,验证 s3
是否是由 s1
和 s2
交错组成的。
s3
是否可以由 s1
和 s2
交错组成。输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出: True
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出: False
dp[i][j]
表示 s1
的前 i
个字符和 s2
的前 j
个字符能否形成 s3
的前 i+j
个字符。s1
,则 dp[i][j] = dp[i-1][j]
且 s1[i-1]
必须等于 s3[i+j-1]
。s2
,则 dp[i][j] = dp[i][j-1]
且 s2[j-1]
必须等于 s3[i+j-1]
。def isInterleave(s1, s2, s3): """ 动态规划判断 s3 是否为 s1 和 s2 交错组成 :param s1: str, 第一个字符串 :param s2: str, 第二个字符串 :param s3: str, 第三个字符串 :return: bool, 是否交错组成 """ l1, l2, l3 = len(s1), len(s2), len(s3) if l1 + l2 != l3: return False dp = [[False] * (l2 + 1) for _ in range(l1 + 1)] dp[0][0] = True for i in range(1, l1 + 1): dp[i][0] = dp[i-1][0] and s1[i-1] == s3[i-1] for j in range(1, l2 + 1): dp[0][j] = dp[0][j-1] and s2[j-1] == s3[j-1] for i in range(1, l1 + 1): for j in range(1, l2 + 1): dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] == s3[i+j-1]) return dp[l1][l2] # 示例调用 print(isInterleave("aabcc", "dbbca", "aadbbcbcac")) # 输出: True
m
和 n
分别为 s1
和 s2
的长度。使用动态规划来具体解释并展示如何验证字符串 s1 = "aabcc"
, s2 = "dbbca"
, 和 s3 = "aadbbcbcac"
是否是交错字符串的过程。这里将详细构建对应的动态规划表格,并用 ASCII 图表来形象化展示。
s1
的前缀。s2
的前缀。(i, j)
表示使用 s1
的前 i
个字符和 s2
的前 j
个字符能否交错组成 s3
的前 i+j
个字符。dp[0][0]
自然为 True(两个空字符串总是可以组成另一个空字符串)。我们将遍历 s3
并逐个填充 dp
表的值,检查是否能通过添加 s1
或 s2
的字符来匹配 s3
的对应位置。
DP Table:
"" d b b c a
"" [T] [F] [F] [F] [F] [F]
a [F] [F] [F] [F] [F] [F]
a [F] [F] [F] [F] [F] [F]
b [F] [F] [F] [F] [F] [F]
c [F] [F] [F] [F] [F] [F]
c [F] [F] [F] [F] [F] [F]
以填充 dp[1][1]
为例:
dp[1][1] = (dp[0][1] and s1[0] == s3[1]) or (dp[1][0] and s2[0] == s3[1])
= (False and 'a' == 'a') or (False and 'd' == 'a') // 给出示例中的字符比较
= False or False
= False
为了简明起见,我们仅展示关键步骤,下面是 s3
完整匹配的 DP 表最后几行状态:
"" d b b c a
"" [T] [F] [F] [F] [F] [F]
a [F] [F] [F] [F] [F] [F]
a [F] [F] [F] [F] [F] [F]
b [F] [F] [T] [T] [F] [F]
c [F] [F] [F] [T] [T] [F]
c [F] [F] [F] [F] [T] [T]
这个 ASCII 表揭示了通过动态规划如何逐步确认 s3
是否能由 s1
和 s2
交错组成。整个过程的可视化有助于理解交错字符串问题的动态规划解决方案的复杂性和执行流程。
s3
。def isInterleave(s1, s2, s3): memo = {} def dfs(i, j, k): if (i, j) in memo: return memo[(i, j)] if k == len(s3): return i == len(s1) and j == len(s2) valid = (i < len(s1) and s1[i] == s3[k] and dfs(i+1, j, k+1)) or \ (j < len(s2) and s2[j] == s3[k] and dfs(i, j+1, k+1)) memo[(i, j)] = valid return valid return dfs(0, 0, 0) # 示例调用 print(isInterleave("aabcc", "dbbca", "aadbbcbcac")) # 输出: True
s1
或 s2
。from collections import deque def isInterleave(s1, s2, s3): if len(s1) + len(s2) != len(s3): return False queue = deque([(0, 0)]) visited = set((0, 0)) while queue: i, j = queue.popleft() if i + j == len(s3): return True if i < len(s1) and s1[i] == s3[i + j] and (i + 1, j) not in visited: queue.append((i + 1, j)) visited.add((i + 1, j)) if j < len(s2) and s2[j] == s3[i + j] and (i, j + 1) not in visited: queue.append((i, j + 1)) visited.add((i, j + 1)) return False # 示例调用 print(isInterleave("aabcc", "dbbca", "aadbbcbcac")) # 输出: True
我们可以通过 ASCII 图解来形象化展示其搜索过程。以下是如何使用 BFS 方法来判断字符串 s1 = "aabcc"
, s2 = "dbbca"
, 和 s3 = "aadbbcbcac"
是否交错组成的一个步骤示例。
在这个方法中,我们将使用一个队列来追踪当前访问的节点位置 (i, j)
,其中 i
是 s1
的索引,j
是 s2
的索引。这样,每个元素的状态可以表示为它是否能由之前的状态推进而来。
我们初始化队列与访问集合来确保不重复处理相同的状态。队列中的每个元素都代表一个待验证的状态,即能否通过当前索引组合 (i, j)
交错组成 s3
的 i+j
部分。
下面是一个简化的 ASCII 表示,说明了状态的扩展过程:
Queue Operations: Initial Queue State: [(0, 0)] // Start from the empty prefixes of s1 and s2 Step 1: Pop (0, 0) Check (1, 0) -> 'a' from s1 matches 'a' from s3 -> Enqueue (1, 0) Check (0, 1) -> 'd' from s2 does not match 'a' from s3 -> Do not enqueue Queue Now: [(1, 0)] Step 2: Pop (1, 0) Check (2, 0) -> 'a' from s1 matches 'a' from s3 -> Enqueue (2, 0) Check (1, 1) -> 'd' from s2 does not match 'a' from s3 -> Do not enqueue Queue Now: [(2, 0)] Step 3: Pop (2, 0) Check (3, 0) -> 'b' from s1 matches 'b' from s3 -> Enqueue (3, 0) Check (2, 1) -> 'd' from s2 does not match 'b' from s3 -> Do not enqueue Queue Now: [(3, 0)] Step 4: Pop (3, 0) Check (4, 0) -> 'c' from s1 matches 'c' from s3 -> Enqueue (4, 0) Check (3, 1) -> 'd' from s2 does not match 'c' from s3 -> Do not enqueue Queue Now: [(4, 0)] Step 5: Pop (4, 0) Check (5, 0) -> Index out of range for s1 -> Do not enqueue Check (4, 1) -> 'a' from s2 matches 'a' from s3 -> Enqueue (4, 1) Queue Now: [(4, 1)] Continue until the queue is empty or (len(s1), len(s2)) is reached, indicating success.
这种 ASCII 图解方式直观地展示了 BFS 方法如何逐步探索不同的状态路径,来验证 s3
是否能由 s1
和 s2
交错形成。通过这样的可视化,可以更容易地理解和教授交错字符串问题的 BFS 解决方案,尤其是在探讨算法课程或演示中。
特征 | 方法一:动态规划 | 方法二:递归(带记忆化) | 方法三:BFS |
---|---|---|---|
时间复杂度 | (O(m * n)) | (O(m * n)) | (O(m * n)) |
空间复杂度 | (O(m * n)) | (O(m * n)) | (O(m * n)) |
优势 | 结构清晰、易于实现 | 避免重复计算、提高效率 | 适合实时解决问题,避免深层递归 |
劣势 | 需要较大空间 | 递归深度可能较大 | 空间利用率不一定高,实现稍复杂 |
这些方法适用于任何需要解析和验证交错字符串的场景,例如编译器的字符串解析、自然语言处理中的句子解构,或者在软件开发中验证输入格式的正确性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。