leetcode面试高频经典 150 题3——字符串的最大公因子

LeetCode 1071. 字符串的最大公因子

题目解析

这道题引入了一个字符串领域的“整除”概念。当一个字符串 t 重复多次可以构成另一个字符串 s 时,我们说 “t 能整除 s”。题目要求我们找到一个最长的字符串 x,它能够同时“整除”给定的两个字符串 str1str2。这个 x 就好比是数字领域的“最大公约数”,因此被称为“字符串的最大公因子”。

核心要点:

  1. 字符串整除: s 是由 t 重复 k 次拼接而成。
  2. 公因子: x 必须同时能整除 str1str2
  3. 最大公因子: 在所有满足条件的 x 中,找到最长的那一个。
  4. 无解情况: 如果不存在这样的 x,返回空字符串 ""

约束条件:

  • 1 <= str1.length, str2.length <= 1000
  • str1str2 仅包含大写英文字母。
示例
  • 示例 1: str1 = "ABCABC", str2 = "ABC"

    • 输出: "ABC"
    • 解释: "ABC" 能整除 str2 (1 次重复),也能整除 str1 (2 次重复)。它是最长的公因子。
  • 示例 2: str1 = "ABABAB", str2 = "ABAB"

    • 输出: "AB"
    • 解释: "AB" 能整除 str2 (2 次重复) 和 str1 (3 次重复)。虽然 "ABAB" 也能整除 str2,但它不能整除 str1,所以公因子是 "AB"
  • 示例 3: str1 = "LEET", str2 = "CODE"

    • 输出: ""
    • 解释: 这两个字符串没有共同的重复单元,不存在公因子。

核心思路:从暴力枚举到数学巧解

  • 解法一:暴力枚举 (直观解法)

    • 思路: 公因子 x 必然是两个字符串的公共前缀。我们可以从长到短枚举 str1 (或 str2) 的所有前缀作为候选 x,然后验证它是否能同时整除 str1str2。第一个找到的就是最长的。
    • 优点: 思路直接,容易实现。
  • 解法二:数学 GCD 法 (最优解)

    • 思路: 这是一个非常巧妙的数学方法。如果存在一个公因子 x,那么 str1str2 一定是由 x 重复 a 次和 b 次构成的。这意味着 str1 + str2str2 + str1 都等于 x 重复 a+b 次,因此它们必然相等。反之,如果 str1 + str2 != str2 + str1,则绝不可能存在公因子。
    • 当这个条件满足时,最大公因子的长度就是两个字符串长度的最大公约数(GCD)。
    • 优点: 效率极高,代码极其简洁。

选择建议:解法二是面试中的标准答案,体现了对问题本质的深刻理解。解法一作为暴力解,可以作为思路的起点。


解法一:暴力枚举前缀

实现步骤:

  1. 确定枚举范围:公因子的长度不可能超过两个字符串中较短者的长度。
  2. 从长到短遍历:从 min(len1, len2)1 开始遍历所有可能的长度 L
  3. 筛选候选前缀:取 str1 中长度为 L 的前缀作为候选公因子 candidate
  4. 验证公因子:检查 candidate 是否能同时整除 str1str2
    • len1len2 必须是 L 的倍数。
    • candidate 重复相应次数,看是否等于 str1str2
  5. 返回结果:一旦找到符合条件的 candidate,立即返回,因为它就是最长的。如果循环结束仍未找到,返回 ""

代码实现 (Python):

class Solution:
    def gcdOfStrings(self, str1: str, str2: str) -> str:
        len1, len2 = len(str1), len(str2)
        
        # 从长到短枚举所有可能的公因子长度
        for L in range(min(len1, len2), 0, -1):
            # 长度必须能同时整除 str1 和 str2 的长度
            if len1 % L == 0 and len2 % L == 0:
                candidate = str1[:L]
                
                # 验证 candidate 是否是公因子
                if candidate * (len1 // L) == str1 and \
                   candidate * (len2 // L) == str2:
                    return candidate
                    
        return ""

复杂度分析:

  • 时间复杂度: O(min⁡(m,n)⋅(m+n))O(\min(m, n) \cdot (m+n))O(min(m,n)(m+n))。在最坏情况下,我们需要遍历 min(m, n) 个候选长度,每次验证都需要 O(m+n)O(m+n)O(m+n) 的时间进行字符串乘法和比较。
  • 空间复杂度: O(m+n)O(m+n)O(m+n)。字符串乘法会产生临时字符串。

解法二:数学 GCD 法 (最优解)

核心洞察:
如果 str1str2 存在公因子 x,那么:
str1 = x * a
str2 = x * b
此时 str1 + str2 = (x * a) + (x * b) = x * (a+b)
str2 + str1 = (x * b) + (x * a) = x * (b+a)
显然,str1 + str2 == str2 + str1

这个性质是充要条件。也就是说,当且仅当 str1 + str2 == str2 + str1 时,它们才存在公因子

实现步骤:

  1. 前置检查:判断 str1 + str2 是否等于 str2 + str1。如果不等,直接返回 "",因为不存在公因子。
  2. 计算长度:如果相等,说明它们共享一个最小的重复单元。最大公因子的长度必然是 gcd(len(str1), len(str2))
  3. 提取结果:从 str1 (或 str2) 中截取长度为 gcd 的前缀,即为所求。

代码实现 (Python):

from math import gcd

class Solution:
    def gcdOfStrings(self, str1: str, str2: str) -> str:
        # 步骤 1: 检查是否存在公因子的根本条件
        if str1 + str2 != str2 + str1:
            return ""
        
        # 步骤 2: 若存在,其最大长度为两字符串长度的最大公约数
        max_len = gcd(len(str1), len(str2))
        
        # 步骤 3: 返回该长度的前缀
        return str1[:max_len]

复杂度分析:

  • 时间复杂度: O(m+n)O(m + n)O(m+n)。主要开销在于字符串的拼接和比较。gcd 函数的时间复杂度为对数级别,远小于字符串操作。
  • 空间复杂度: O(m+n)O(m + n)O(m+n)。用于存储拼接后的临时字符串。

总结与对比

特性解法一 (暴力枚举)解法二 (数学 GCD 法)
核心思想从长到短检查所有公共前缀利用 s1+s2 == s2+s1 性质和长度的 GCD
时间复杂度O(min⁡(m,n)⋅(m+n))O(\min(m, n) \cdot (m+n))O(min(m,n)(m+n))O(m+n)O(m+n)O(m+n)
代码简洁性较复杂非常简洁
推荐度较低(作为保底方案)强烈推荐(面试首选)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快撑死的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值