模式匹配

标签: 数学 字符串 回溯 枚举

难度: Medium

你有两个字符串,即patternvaluepattern字符串由字母"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仅包含小写字母。

Submission

运行时间: 29 ms

内存: 16.1 MB

class Solution:
    def patternMatching(self, pattern: str, value: str) -> bool:
        cnta = pattern.count('a')
        cntb = len(pattern) - cnta
        if not value:
            return not (cnta and cntb)
        if cnta == 1 or cntb == 1:
            return True
        n = len(value)
        if not (cnta and cntb):
            c = cnta or cntb
            if n % c:
                return False
            s = value[:n // c]
            return bool(re.match(f'{s}{{{c}}}', value))

        for len_a in range(n):
            total_a = len_a * cnta
            rest = n - total_a
            len_b, t = divmod(rest, cntb)
            if t:
                continue
            d = {'a':[None, len_a], 'b':[None, len_b]}
            i = 0
            for c in pattern:
                if d[c][0] is None:
                    d[c][0] = value[i:i + d[c][1]]
                    if d['a'][0] == d['b'][0]:
                        break
                elif value[i:i + d[c][1]] != d[c][0]:
                    break
                i += d[c][1]
            else:
                return True
        return False



Explain

此题解采用模拟和试探的方法来决定两种模式 'a' 和 'b' 可能对应的字符串长度,通过穷举所有可能的长度组合。首先计算 pattern 中 'a' 和 'b' 的出现次数。如果 value 为空字符串,只有在 pattern 也不含 'a' 或 'b' 时才返回真。特殊情况处理,若 'a' 或 'b' 出现次数为1,那么总可以将 value 分配给那个模式字符。接下来,尝试每一个可能的 'a' 的长度 (len_a),计算相应的 'b' 的长度。通过字典 d 存储 'a' 和 'b' 对应的字符串和长度。遍历 pattern,检查对应段的字符串是否匹配,如果遇到不匹配的情况则结束当前循环。如果整个 pattern 遍历完成也没有中断,说明找到了一种匹配方式。

时间复杂度: O(n^2)

空间复杂度: O(1)

class Solution:
    def patternMatching(self, pattern: str, value: str) -> bool:
        cnta = pattern.count('a')  # 统计 'a' 的数量
        cntb = len(pattern) - cnta  # 统计 'b' 的数量
        if not value:  # 如果 value 为空
            return not (cnta and cntb)  # 只有 'a' 或 'b' 其中一个为0时返回 True
        if cnta == 1 or cntb == 1:  # 如果 'a' 或 'b' 的数量为1
            return True  # 直接返回 True
        n = len(value)  # value 的长度
        if not (cnta and cntb):  # 如果 'a' 或 'b' 其中一个为0
            c = cnta or cntb  # 获取非零的数量
            if n % c:  # 如果不能整除则返回 False
                return False
            s = value[:n // c]  # 取得对应的子串
            return bool(re.match(f'{s}{{{c}}}', value))  # 检查模式是否匹配
        for len_a in range(n):  # 尝试每一个可能的 'a' 的长度
            total_a = len_a * cnta  # 计算 'a' 对应的总长度
            rest = n - total_a  # 剩余长度
            len_b, t = divmod(rest, cntb)  # 计算 'b' 的长度和剩余部分
            if t:  # 如果剩余部分不为0,继续下一轮
                continue
            d = {'a':[None, len_a], 'b':[None, len_b]}  # 存储 'a' 和 'b' 的字符串和长度
            i = 0
            for c in pattern:  # 遍历 pattern
                if d[c][0] is None:  # 如果还没有确定对应的字符串
                    d[c][0] = value[i:i + d[c][1]]  # 确定对应的字符串
                    if d['a'][0] == d['b'][0]:  # 如果 'a' 和 'b' 对应相同的字符串
                        break
                elif value[i:i + d[c][1]] != d[c][0]:  # 如果当前段不匹配
                    break
                i += d[c][1]  # 更新下一段的起始索引
            else:  # 如果遍历完 pattern 没有中断
                return True  # 返回 True
        return False  # 如果尝试所有可能都失败,返回 False

Explore

在枚举'a'和'b'的长度时,选择'a'的长度从0到value的长度是因为'a'的最大可能长度不会超过value的总长度。若'a'的长度超过了value的长度,那么'a'对应的字符串将无法从value中提取,因此'a'的长度范围设定为0到value长度。同样的逻辑也适用于'b'。这样的范围设定确保了所有可能的长度组合都被考虑到,同时避免了无意义的枚举,提高了算法的效率。

当'a'和'b'在pattern中的数量不同,算法通过调整'a'和'b'对应字符串的长度来处理这种不平衡。通过枚举可能的'a'长度,并计算对应的'b'长度(通过剩余长度除以'b'的数量得到)。这个长度计算方法确保了不同数量的'a'和'b'能够被适当地映射到value的不同部分。这种处理方式虽然复杂,但能够有效地处理不同数量对应关系,不会影响最终的匹配结果,只要存在一种有效的长度组合能够满足模式匹配。

算法中断言'a'和'b'不指向相同的字符串是为了确保模式中的不同元素对应到value中的不同部分,这样才能实现真正的模式匹配。如果'a'和'b'指向相同的字符串,那么模式中'a'和'b'的区分将失去意义,无法完成正确的匹配。这种假设确实可能在某些极端情况下限制匹配,例如当模式和字符串都允许'a'和'b'对应相同的字符串时,但这种情况在实际应用中较为罕见,通常认为'a'和'b'应该映射到不同的字符串才符合模式匹配的初衷。