LeetCode 1071. 字符串的最大公因子
题目解析
这道题引入了一个字符串领域的“整除”概念。当一个字符串 t 重复多次可以构成另一个字符串 s 时,我们说 “t 能整除 s”。题目要求我们找到一个最长的字符串 x,它能够同时“整除”给定的两个字符串 str1 和 str2。这个 x 就好比是数字领域的“最大公约数”,因此被称为“字符串的最大公因子”。
核心要点:
- 字符串整除:
s是由t重复k次拼接而成。 - 公因子:
x必须同时能整除str1和str2。 - 最大公因子: 在所有满足条件的
x中,找到最长的那一个。 - 无解情况: 如果不存在这样的
x,返回空字符串""。
约束条件:
1 <= str1.length, str2.length <= 1000str1和str2仅包含大写英文字母。
示例
-
示例 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,然后验证它是否能同时整除str1和str2。第一个找到的就是最长的。 - 优点: 思路直接,容易实现。
- 思路: 公因子
-
解法二:数学 GCD 法 (最优解)
- 思路: 这是一个非常巧妙的数学方法。如果存在一个公因子
x,那么str1和str2一定是由x重复a次和b次构成的。这意味着str1 + str2和str2 + str1都等于x重复a+b次,因此它们必然相等。反之,如果str1 + str2 != str2 + str1,则绝不可能存在公因子。 - 当这个条件满足时,最大公因子的长度就是两个字符串长度的最大公约数(GCD)。
- 优点: 效率极高,代码极其简洁。
- 思路: 这是一个非常巧妙的数学方法。如果存在一个公因子
选择建议:解法二是面试中的标准答案,体现了对问题本质的深刻理解。解法一作为暴力解,可以作为思路的起点。
解法一:暴力枚举前缀
实现步骤:
- 确定枚举范围:公因子的长度不可能超过两个字符串中较短者的长度。
- 从长到短遍历:从
min(len1, len2)到1开始遍历所有可能的长度L。 - 筛选候选前缀:取
str1中长度为L的前缀作为候选公因子candidate。 - 验证公因子:检查
candidate是否能同时整除str1和str2。len1和len2必须是L的倍数。- 将
candidate重复相应次数,看是否等于str1和str2。
- 返回结果:一旦找到符合条件的
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 法 (最优解)
核心洞察:
如果 str1 和 str2 存在公因子 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 时,它们才存在公因子。
实现步骤:
- 前置检查:判断
str1 + str2是否等于str2 + str1。如果不等,直接返回"",因为不存在公因子。 - 计算长度:如果相等,说明它们共享一个最小的重复单元。最大公因子的长度必然是
gcd(len(str1), len(str2))。 - 提取结果:从
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) |
| 代码简洁性 | 较复杂 | 非常简洁 |
| 推荐度 | 较低(作为保底方案) | 强烈推荐(面试首选) |


被折叠的 条评论
为什么被折叠?



