程序员面试金典 - 面试题 16.18. 模式匹配

题目难度: 中等

原题链接

今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

你有两个字符串,即 pattern 和 value。 pattern 字符串由字母"a"和"b"组成,用于描述字符串中的模式。例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat"是"a",“go"是"b”),该字符串也匹配像"a"、"ab"和"b"这样的模式。但需注意"a"和"b"不能同时表示相同的字符串。编写一个方法判断 value 字符串是否匹配 pattern 字符串。

示例 1:

  • 输入: pattern = “abba”, value = “dogcatcatdog”
  • 输出: true

示例 2:

  • 输入: pattern = “abba”, value = “dogcatcatfish”
  • 输出: false

示例 3:

  • 输入: pattern = “aaaa”, value = “dogcatcatdog”
  • 输出: false

示例 4:

  • 输入: pattern = “abba”, value = “dogdogdogdog”
  • 输出: true
  • 解释: “a”=“dogdog”,b="",反之也符合规则

提示:

  • 1 <= len(pattern) <= 1000
  • 0 <= len(value) <= 1000
  • 你可以假设 pattern 只包含字母"a"和"b",value 仅包含小写字母。

题目思考

  1. 如何快速得出 a 和 b 的有效映射?
  2. 有哪些特殊情况?

解决方案

思路

  • 分析题目, 假设 value 字符串长度为 n, 而 pattern 字符串包含 ca 个 a, cb 个 b, a 和 b 映射后的字符串的长度分别为 lea 和 leb, 那么有n = ca*lea+cb*leb
  • 根据上述公式穷举, ca 和 cb 是已知的, 我们只需要从 0 到 n 遍历所有可能的 lea 即可, leb 可以通过上式计算出来, 且需要是非负整数
  • 然后根据有效的 lea 和 leb 组合, 我们就可以通过遍历 value 字符串, 来得到映射后的字符串, 以及该长度组合下两字符串是否能够完全匹配
  • 最后特别注意四种特殊情况:
    1. ca 和 cb 都是 0, 此时只有当 value 字符串为空时才匹配
    2. ca 是 0, 此时可以直接得出 b 映射后的字符串, 从而进一步计算是否匹配
    3. cb 是 0, 此时可以直接得出 a 映射后的字符串, 从而进一步计算是否匹配
    4. a 和 b 映射后的字符串相同, 根据题意这种情况无效
  • 下面的代码对必要步骤有详细的解释, 方便大家理解

复杂度

  • 时间复杂度 O(N^2): N 是 value 字符串长度, 外层和内层循环都需要遍历 N
  • 空间复杂度 O(1): 只使用了常数空间的变量

代码

class Solution:
    def patternMatching(self, pattern: str, value: str) -> bool:
        n = len(value)
        # 首先统计pattern中a和b的数目ca和cb
        cnts = collections.Counter(pattern)
        ca, cb = cnts["a"], cnts["b"]
        if ca == 0 and cb == 0:
            # 特殊情况1: pattern是空字符串, 此时只有当value也是空才返回true
            return not value
        if ca == 0:
            # 特殊情况2: pattern中没有a, 则直接计算value是否匹配b (value是空, 或者等于cb个b的重复)
            return n == 0 or n % cb == 0 and value == cb * value[: n // cb]
        if cb == 0:
            # 特殊情况3: pattern中没有b, 则直接计算value是否匹配a (value是空, 或者等于ca个a的重复)
            return n == 0 or n % ca == 0 and value == ca * value[: n // ca]
        # 此时说明ca和cb都大于0
        # 开始从0到n遍历字符串a的长度lea
        for lea in range(n + 1):
            if ca * lea > n:
                # 当前a的总长度已经大于n了, 当前和更大的长度肯定都无效, 直接break
                break
            if (n - ca * lea) % cb == 0:
                # 剩余部分能整除cb, 可能存在有效解, 计算得出b的长度leb
                leb = (n - ca * lea) // cb
                # i是当前value字符串的起始下标
                i = 0
                # a和b分别是pattern中a和b对应到value的字符串, 初始化为空
                a, b = "", ""
                for x in pattern:
                    if x == "a":
                        if a == "" or value[i : i + lea] == a:
                            # a尚未被定义, 或者等于现在区间, 匹配有效, 继续循环
                            a = value[i : i + lea]
                            i += lea
                        else:
                            # 否则说明遇到不匹配, 跳出循环
                            break
                    else:
                        if b == "" or value[i : i + leb] == b:
                            # b尚未被定义, 或者等于现在区间, 匹配有效, 继续循环
                            b = value[i : i + leb]
                            i += leb
                        else:
                            # 否则说明遇到不匹配, 跳出循环
                            break
                else:
                    if a == b:
                        # 特殊情况4: a和b映射后的字符串相同, 无效
                        continue
                    # 此时说明a和b不相同, 且value匹配对应的pattern, 有效
                    return True
        return False

大家可以在下面这些地方找到我~

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~

程序员面试金典 - 面试题 16.18. 模式匹配_第1张图片

你可能感兴趣的:(Leetcode,面试,职场和发展,leetcode)