问题描述
小M在研究字符串时发现了一个有趣的现象:某些字符串是由一个较短的子串反复拼接而成的。如果能够找到这个最短的子串,便可以很好地还原字符串的结构。你的任务是给定一个字符串,判断它是否是由某个子串反复拼接而成的。如果是,输出该最短的子串;否则,输出空字符串""。
例如:当输入字符串为 abababab 时,它可以由子串 ab 反复拼接而成,因此输出 ab;而如果输入 ab,则该字符串不能通过子串的重复拼接得到,因此输出空字符串。
思路
-
理解问题:我们需要找到一个子串,这个子串可以重复拼接直到构成整个字符串。这种字符串通常被称为“由重复子串组成的字符串”。
-
如何判断一个字符串是否可以由某个子串重复拼接而成:
- 如果一个字符串
inp能够由某个子串s重复拼接,那么字符串的长度应该是子串长度的整数倍。 - 比如,
abcabcabcabc就是由abc这个子串重复拼接而成的。我们可以通过检查不同长度的子串来验证这一点。
- 如果一个字符串
-
解题步骤:
- 从长度为1的子串开始,逐步检查每个可能的子串,直到字符串长度的一半为止(因为超过一半长度的子串再重复也无法覆盖整个字符串)。
- 对于每个子串,检查它是否可以通过重复拼接成原字符串。如果可以,就返回该子串;如果不行,则继续检查下一个子串。
- 如果最终没有找到合适的子串,就返回空字符串。
-
优化点:我们只需要检查长度从1到
len(inp)//2的子串,因为任何长度大于一半的子串都不可能重复拼接成一个长度为n的字符串。
解题思考
假设输入的字符串 inp 长度为 n,我们可以枚举所有可能的子串长度 length,从1开始,直到 n//2。对于每个 length,我们检查该子串能否重复拼接得到 inp。如果能,就返回该子串;如果遍历完所有子串都找不到符合条件的,就返回空字符串。
代码详解
def solution(inp):
# 遍历可能的子串长度,从1到 len(inp)//2
for length in range(1, len(inp) // 2 + 1):
# 如果原字符串的长度能被当前子串长度整除
if len(inp) % length == 0:
# 获取当前长度的子串
substring = inp[:length]
# 通过重复子串构建一个新的字符串
repeated_string = substring * (len(inp) // length)
# 比较构建的字符串与原字符串是否相同
if repeated_string == inp:
return substring
# 如果没有找到符合条件的子串,返回空字符串
return ""
if __name__ == "__main__":
print(solution("abcabcabcabc") == "abc")
print(solution("aaa") == "a")
print(solution("abababab") == "ab")
print(solution("ab") == "")
print(solution("abcdabcdabcdabcd") == "abcd")
print(solution("b") == "")
代码解析
-
循环遍历子串长度:从1到
len(inp)//2,我们逐一尝试不同长度的子串。对于每个长度,首先检查它能否整除原字符串的长度。如果不能整除,说明这个长度的子串不能组成原字符串,因此跳过。 -
构建重复的字符串:如果当前的子串长度能整除字符串的长度,我们就用当前子串重复拼接,看看能否构成原字符串。
-
返回结果:如果找到了符合条件的子串,直接返回它;如果没有找到,最终返回空字符串。
详解
以字符串 abcabcabcabc 为例,长度为 12,我们从长度为 1 到 6 的子串进行尝试。
- 长度1:
a,构造的字符串是a重复 12 次,显然不对。 - 长度2:
ab,构造的字符串是ab重复 6 次,结果是abababababab,不对。 - 长度3:
abc,构造的字符串是abc重复 4 次,结果是abcabcabcabc,与原字符串一致,返回abc。
总结
- 从小到大检查子串的有效性:我们从可能的最短子串开始,逐步检查,直到找到第一个符合条件的子串,这样可以尽早退出,避免无效计算。
- 字串拼接的核心原理:这个题目本质上是在考察如何通过字符串的重复结构来还原其组成。
- 时间复杂度:最坏情况下,我们会遍历所有长度从
1到n//2的子串,这样的时间复杂度大约是 O(n^2),因为每次构建重复的字符串需要 O(n) 的时间。