字符串最短循环子串问题的解法与分析
一、问题描述
小M在研究字符串时发现了一个有趣的现象:某些字符串是由一个较短的子串反复拼接而成的。如果能够找到这个最短的子串,便可以很好地还原字符串的结构。你的任务是给定一个字符串,判断它是否是由某个子串反复拼接而成的。如果是,输出该最短的子串;否则,输出空字符串""。
例如:当输入字符串为 abababab 时,它可以由子串 ab 反复拼接而成,因此输出 ab;而如果输入 ab,则该字符串不能通过子串的重复拼接得到,因此输出空字符串。
测试样例
样例1:
输入:
inp = "abcabcabcabc"
输出:'abc'
二、解题思路
如果没有思路,可以先询问豆包提供一些思路提示和伪代码分析。
根据提示,我们可以思考出实现逻辑:
- 首先,我们需要确定可能的最短子串长度范围。由于最短循环子串的长度一定小于等于字符串的一半,所以我们从长度为 1 开始遍历到字符串长度的一半。
- 对于每个可能的子串长度
len,我们检查字符串的长度是否能被len整除。如果不能整除,那么这个长度的子串不可能是循环子串。 - 如果长度
len满足条件,我们提取出字符串的前len个字符作为候选子串candidate。 - 然后,我们从
len的位置开始,每次向后移动len个字符,检查提取出的子串是否与candidate相等。如果有不相等的情况,说明这个长度的子串不是循环子串,我们继续尝试下一个长度。 - 如果找到了一个满足条件的子串长度,我们就返回这个最短的循环子串;如果遍历完所有可能的长度都没有找到,我们返回空字符串。
三、代码实现
以下是使用 Java 实现的代码:
public class ShortestRepeatingSubstring {
public static String solution(String inp) {
int n = inp.length();
for (int len = 1; len <= n/2; len++) {
if (n % len == 0) {
boolean isRepeated = true;
String candidate = inp.substring(0, len);
for (int i = len; i < n; i += len) {
if (!inp.substring(i, i + len).equals(candidate)) {
isRepeated = false;
break;
}
}
if (isRepeated) {
return candidate;
}
}
}
return "";
}
}
- 外层循环(控制子串长度尝试) :
for (int len = 1; len <= n / 2; len++),这里我们从长度为 1 的子串开始尝试,最大尝试到字符串长度的一半。因为如果存在循环子串,其长度不会超过原字符串长度的一半。 - 条件判断(字符串长度是否能被当前子串长度整除) :
if (n % len == 0),只有当字符串长度能被当前尝试的子串长度整除时,才有可能是由该长度的子串重复组成。 - 获取当前尝试的子串:
String candidate = inp.substring(0, len);,通过substring方法获取从字符串开头到当前尝试长度的子串作为候选子串。 - 内层循环(检查后续子串是否与候选子串相等) :
for (int i = len; i < n; i += len),从当前子串长度位置开始,以子串长度为步长,检查后续的子串是否与候选子串相等。如果有不相等的情况,就将isRepeated标记为false并跳出循环。 - 最终结果判断:如果
isRepeated为true,说明找到了最短循环子串,直接返回该候选子串;如果遍历完所有可能的子串长度都没有找到,就返回空字符串。
五、时间复杂度分析
- 外层循环最多执行
n/2次(n为字符串长度),对于每个外层循环中的子串长度尝试,内层循环最多执行n/len次。 - 所以时间复杂度大致为 O(n^2),这种方法在处理较长字符串时效率可能会受到一定影响,可以考虑使用更优化的算法,比如 KMP 算法的一些变形来进一步提高效率。
六、空间复杂度分析
代码中除了存储输入字符串外,只使用了有限的额外空间,所以空间复杂度为O(1)。
七、总结
通过这个问题,我们可以看到如何使用简单的循环和字符串操作来解决一个有趣的字符串问题。同时,我们也可以思考是否有更高效的算法来解决这个问题,比如使用哈希表或者动态规划等方法。
希望这篇文章对你理解字符串最短循环子串问题有所帮助!如果你有任何问题或建议,欢迎在评论区留言。