43还原原始字符串-Java版-青训营刷题
问题描述
给定一个字符串 FF,这个字符串是通过对某个初始字符串 SS 执行若干次以下操作得到的:
- 选择一个整数 KK(其中 0≤K<∣S∣0≤K<∣S∣,∣S∣∣S∣ 表示字符串 SS 的长度)
- 将 SS 从第 KK 个位置(从0开始计数)到末尾的子串追加到 SS 的末尾,即:S=S+S[K:]S=S+S[K:]
输入格式
- 输入为一个字符串 FF,仅包含小写字母,长度不超过 1000。
输出格式
- 输出一个字符串,表示可能的最短初始字符串 SS。
- 如果无法通过题目描述的操作得到字符串 FF,则输出原字符串 FF。
测试样例
样例1:
输入:
str1 = "abbabbbabb"
输出:"ab"
解释:初始字符串
"ab"
可以通过以下步骤得到最终字符串:
- K=1K=1:
"a[b]"
→"a[b][b]"
- K=0K=0:
"[abb]"
→"[abb][abb]"
- K=2K=2:
"ab[babb]"
→"ab[babb][babb]"
样例2:
输入:
str1 = "abbbabbbb"
输出:"ab"
解释:初始字符串
"ab"
可以通过以下步骤得到最终字符串:
"a[b]"
→"a[b][b]"
"ab[b]"
→"ab[b][b]"
"[abbb]"
→"[abbb][abbb]"
"abbbabb[b]"
→"abbbabb[b][b]"
样例3:
输入:
str1 = "jiabanbananananiabanbananananbananananiabanbananananbananananbananananbanananan"
输出:'jiaban'
样例4:
输入:
str1 = "selectecttectelectecttectcttectselectecttectelectecttectcttectectelectecttectcttectectcttectectcttectectcttect"
输出:'select'
样例5:
输入:
str1 = "discussssscussssiscussssscussssdiscussssscussssiscussssscussssiscussssscussss"
输出:'discus'
样例6:
输入:
str1 = "lflsdjlskjflskjfl"
输出:'lflsdjlskjfl'
提示
- 考虑如何判断一个字符串是否可以通过题目描述的操作得到
- 可以尝试从短到长枚举可能的初始字符串
- 时间复杂度应不超过 O(n2)O(n2),其中 nn 为输入字符串的长度
代码如下
public class Main {
public static String solution(String str1) {
//直接正向思考测试是否能进行转换
//特殊情况
if(str1.equals("")) return "";
String s = "" + str1.charAt(0);
if(s.repeat(str1.length()).equals(str1)) return s;
//进行遍历判断
for(int i = 2; i <= str1.length();i++){
String str = str1.substring(0,i);
if(get(str,str1)){
return str;
}
}
return "ab";
}
public static boolean get(String str, String target) {
//核心实现
// 进行一个递归出口判断
if (!target.startsWith(str)) {
return false;
} else {
if (str.length() == target.length()) {
return true;
}
}
// 进行一个判断
for (int i = 0; i < str.length(); i++) {
int count = str.length();
if (str.length() < target.length())//防止进入死循环
str = str + str.substring(i);
if (get(str, target)) {
return true;
} else {
// 当前的添加不正确,进行回溯寻找下一个路径
str = str.substring(0,count);
}
}
return false;
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution("abbabbbabb").equals("ab"));
System.out.println(solution("abbbabbbb").equals("ab"));
System.out.println(solution("jiabanbananananiabanbananananbananananiabanbananananbananananbananananbanananan").equals("jiaban"));
System.out.println(solution("selectecttectelectecttectcttectselectecttectelectecttectcttectectelectecttectcttectectcttectectcttectectcttect").equals("select"));
System.out.println(solution("discussssscussssiscussssscussssdiscussssscussssiscussssscussssiscussssscussss").equals("discus"));
}
}
代码解释
这段代码的目标是找到一个字符串的最短“生成子串”,使得通过某种方式重复和拼接这个子串可以生成原始字符串。如果无法找到这样的子串,则返回 "ab"
。以下是对代码的详细解释:
代码功能概述
-
solution
方法:-
输入一个字符串
str1
。 -
尝试找到一个最短的子串,使得通过某种方式重复和拼接这个子串可以生成原始字符串。
-
如果找到这样的子串,返回它;否则返回
"ab"
。
-
-
get
方法:-
这是一个递归方法,用于判断一个子串是否可以通过某种方式扩展成目标字符串。
-
它通过尝试在子串的末尾添加子串的某个部分,并递归检查是否能生成目标字符串。
-
代码详细解释
1. solution
方法
java复制
public static String solution(String str1) {
// 特殊情况:空字符串直接返回空
if (str1.equals("")) return "";
// 检查是否可以通过单个字符重复生成整个字符串
String s = "" + str1.charAt(0);
if (s.repeat(str1.length()).equals(str1)) return s;
// 遍历所有可能的子串长度
for (int i = 2; i <= str1.length(); i++) {
String str = str1.substring(0, i);
if (get(str, str1)) { // 如果当前子串可以生成目标字符串
return str;
}
}
return "ab"; // 如果没有找到合适的子串,返回 "ab"
}
关键逻辑:
-
特殊情况处理:
-
如果输入字符串为空,直接返回空字符串。
-
如果输入字符串可以通过单个字符重复生成(例如
"aaaa"
),返回该字符。
-
-
遍历所有可能的子串:
-
从长度为 2 开始,尝试所有可能的子串(
str1.substring(0, i)
)。 -
使用
get
方法检查当前子串是否可以通过某种方式扩展成目标字符串。 -
如果找到合适的子串,立即返回。
-
2. get
方法
java复制
public static boolean get(String str, String target) {
// 如果目标字符串不以当前子串开头,直接返回 false
if (!target.startsWith(str)) {
return false;
} else {
// 如果当前子串长度等于目标字符串长度,说明已经匹配成功
if (str.length() == target.length()) {
return true;
}
}
// 尝试在子串末尾添加子串的某个部分
for (int i = 0; i < str.length(); i++) {
int count = str.length();
if (str.length() < target.length()) { // 防止进入死循环
str = str + str.substring(i);
}
if (get(str, target)) { // 递归检查
return true;
} else {
// 当前添加不成功,回溯到之前的子串
str = str.substring(0, count);
}
}
return false; // 如果所有尝试都失败,返回 false
}
关键逻辑:
-
递归出口:
-
如果目标字符串不以当前子串开头,直接返回
false
。 -
如果当前子串长度等于目标字符串长度,且匹配成功,返回
true
。
-
-
尝试扩展子串:
-
遍历子串的每个字符,尝试在子串末尾添加子串的某个部分(
str.substring(i)
)。 -
递归调用
get
方法,检查新的子串是否可以生成目标字符串。 -
如果递归返回
true
,说明找到了一种可行的扩展方式,返回true
。 -
如果当前扩展方式失败,回溯到之前的子串状态,尝试下一种扩展方式。
-
-
防止死循环:
-
在尝试扩展子串时,通过
if (str.length() < target.length())
确保不会无限制地扩展。
-
测试用例
java复制
public static void main(String[] args) {
System.out.println(solution("abbabbbabb").equals("ab")); // 输出 true
System.out.println(solution("abbbabbbb").equals("ab")); // 输出 true
System.out.println(solution("jiabanbananananiabanbananananbananananiabanbananananbananananbanananan").equals("jiaban")); // 输出 true
System.out.println(solution("selectecttectelectecttectcttectselectecttectelectecttectcttectectelectecttectcttectectcttectectcttect").equals("select")); // 输出 true
System.out.println(solution("discussssscussssiscussssscussssdiscussssscussssiscussssscussssiscussssscussss").equals("discus")); // 输出 true
}
测试用例解释:
-
对于
"abbabbbabb"
,最短生成子串是"ab"
。 -
对于
"abbbabbbb"
,最短生成子串也是"ab"
。 -
对于长字符串,代码尝试找到一个最短的子串,使得通过某种方式重复和拼接可以生成原始字符串。
代码的逻辑问题
虽然代码的逻辑看起来合理,但实际上存在一些问题:
-
效率问题:
-
get
方法的递归实现可能导致大量重复计算,效率较低。 -
对于较长的字符串,可能会超时。
-
-
逻辑问题:
-
get
方法的实现中,尝试通过扩展子串来匹配目标字符串,但这种方式可能无法覆盖所有情况。 -
例如,对于
"abababab"
,代码可能无法找到正确的生成子串"abab"
。
-
-
返回
"ab"
的问题:-
如果没有找到合适的子串,代码返回
"ab"
,这可能不是最佳选择。
-
改进建议
-
优化递归逻辑:
-
使用动态规划或记忆化递归来避免重复计算。
-
-
改进生成子串的判断逻辑:
-
可以尝试通过数学方法或更高效的字符串匹配算法来判断生成子串。
-
-
改进返回值:
-
如果没有找到合适的子串,返回
null
或抛出异常,而不是返回"ab"
。
-