1. 简介
在本教程中,我们将展示如何在 Java 中判断一个字符串是否是由某个子串重复构成的。
2. 问题定义
我们先明确一些前提条件:
- 字符串长度至少为2
- 子串至少重复一次
举个例子:
✅ 以下字符串满足条件:
"aa"
"ababab"
"barrybarrybarry"
❌ 以下字符串不满足条件:
"aba"
"cbacbac"
"carlosxcarlosy"
我们将通过几种不同方式来解决这个问题。
3. 简单粗暴的实现(暴力解法)
思路如下:
- 遍历字符串前半部分字符,构建可能的子串
- 每次尝试用当前子串替换原字符串中的所有匹配项
- 如果替换后字符串为空,说明原字符串完全由该子串构成
示例代码如下:
public static boolean containsOnlySubstrings(String string) {
if (string.length() < 2) {
return false;
}
StringBuilder substr = new StringBuilder();
for (int i = 0; i < string.length() / 2; i++) {
substr.append(string.charAt(i));
String clearedFromSubstrings = string.replaceAll(substr.toString(), "");
if (clearedFromSubstrings.length() == 0) {
return true;
}
}
return false;
}
✅ 测试用例:
String validString = "aa";
String validStringTwo = "ababab";
String validStringThree = "baeldungbaeldung";
String invalidString = "aca";
String invalidStringTwo = "ababa";
String invalidStringThree = "baeldungnonrepeatedbaeldung";
✅ 验证代码:
assertTrue(containsOnlySubstrings(validString));
assertTrue(containsOnlySubstrings(validStringTwo));
assertTrue(containsOnlySubstrings(validStringThree));
assertFalse(containsOnlySubstrings(invalidString));
assertFalse(containsOnlySubstrings(invalidStringTwo));
assertFalse(containsOnlySubstrings(invalidStringThree));
⚠️ 踩坑提醒:
这个方法虽然逻辑简单,但性能较差,因为 replaceAll
在每次循环中都会执行一次正则匹配,整体时间复杂度为 O(n²),不建议用于长字符串。
4. 高效解法(巧妙解法)
我们换个思路:
如果一个字符串是由某个子串重复构成的,那么它一定是其自身的某种“旋转”。
比如 "ababab"
是 "ab"
的重复拼接,也可以看作是 "ab"
的多次旋转拼接。
有个定理可以帮我们简化判断:
如果字符串 A 和 B 长度相同,那么 A 是 B 的旋转字符串当且仅当 A 是 B+B 的子串
所以我们只需要判断原字符串是否出现在 (string + string).substring(1, ...)
中即可。
✅ 实现代码如下:
public static boolean containsOnlySubstringsEfficient(String string) {
return ((string + string).indexOf(string, 1) != string.length());
}
✅ 用法和测试方式与前面一致,但这次时间复杂度降到了 O(n),性能更优。
💡 小贴士:
这个技巧在字符串算法中很经典,常用于判断重复结构,比如在 LeetCode 上也有类似题目。
5. 总结
我们介绍了两种判断字符串是否由重复子串构成的方法:
方法 | 时间复杂度 | 说明 |
---|---|---|
暴力解法 | O(n²) | 思路简单,适合短字符串 |
高效解法 | O(n) | 利用字符串旋转定理,效率更高,推荐使用 |
所有示例代码均可在 GitHub 仓库 中找到。