Java 递归字符串解压缩
我正在尝试解压缩如下所示的字符串: 输入:4(ab) 输出:abababab 输入:11ab 输出:AAAAAAB 输入:2(3b3(ab)) 输出:BBBABBBAB 使用下面的递归方法,上面的示例都能正确显示,但当我输入以下内容时会出现问题: 输入:4(ab)a 预期产出:阿巴巴 输入:2(3b3(ab))a 预期产量:BBBABABBBABA 我意识到在返回声明中出现的问题是“返回重复”。在其当前状态下,递归将继续,直到到达输入字符串的末尾,即使在结束括号之后也是如此。基本上,我不知道如何让它在到达结束括号时中断,然后在有任何剩余时继续。在2(3b3(ab))a中,它应该返回2*(3b3(ab))+a,现在它返回2*(3b3(ab))a。非常感谢您的任何帮助,因为我无法理解Java 递归字符串解压缩,java,algorithm,recursion,nested,compression,Java,Algorithm,Recursion,Nested,Compression,我正在尝试解压缩如下所示的字符串: 输入:4(ab) 输出:abababab 输入:11ab 输出:AAAAAAB 输入:2(3b3(ab)) 输出:BBBABBBAB 使用下面的递归方法,上面的示例都能正确显示,但当我输入以下内容时会出现问题: 输入:4(ab)a 预期产出:阿巴巴 输入:2(3b3(ab))a 预期产量:BBBABABBBABA 我意识到在返回声明中出现的问题是“返回重复”。在其当前状态下,递归将继续,直到到达输入字符串的末尾,即使在结束括号之后也是如此。基本上,我不知道如何
public static String decompress(String compressedText) throws Exception
{
//BASE CASE
if(compressedText.length() == 1)
{
if(compressedText.charAt(0) == ')')
{
System.out.println("1: " + compressedText);
return "";
}
else
{
System.out.println("2: " + compressedText);
return compressedText;
}
}
//END BASECASE
if(compressedText.charAt(0) == '(')
{
System.out.println("3: " + compressedText);
return decompress(compressedText.substring(1));
}
//IF DOUBLE DIGIT
if(Character.isDigit(compressedText.charAt(0)) == true && Character.isDigit(compressedText.charAt(1)) == true)
{
if(compressedText.charAt(3) != '(')
{
System.out.println("4: " + compressedText);
int i = Integer.parseInt(compressedText.substring(0,2));
String repeated = new String(new char[i]).replace("\0", compressedText.substring(2,3));
return repeated + decompress(compressedText.substring(3));
}
else
{
System.out.println("5: " + compressedText);
int i = Integer.parseInt(compressedText.substring(0,2));
String repeated = new String(new char[i]).replace("\0", decompress(compressedText.substring(2)));
return repeated;
}
}
//END DOUBLE DIGIT
//IF SINGLE DIGIT
if (Character.isDigit(compressedText.charAt(0)) == true)
{
if(compressedText.charAt(1) !='(')
{
System.out.println("6: " + compressedText);
int i = Integer.parseInt(compressedText.substring(0,1));
String repeated = new String(new char[i]).replace("\0", compressedText.substring(1,2));
return repeated + decompress(compressedText.substring(2));
}
else
{
System.out.println("7: " + compressedText);
int i = Integer.parseInt(compressedText.substring(0,1));
String repeated = new String(new char[i]).replace("\0", decompress(compressedText.substring(1)));
return repeated;
}
}
//END SINGLE DIGIT
//IF RIGHT PARENTHESIS
if (compressedText.charAt(0) == ')')
{
if (compressedText.charAt(1) != ')')
{
System.out.println("8: " + compressedText);
return "";
}
else
{
System.out.println("9: " + compressedText);
return decompress(compressedText.substring(1));
}
}
//END
System.out.println("10: " + compressedText);
return compressedText.charAt(0)+decompress(compressedText.substring(1));
}
我注意到的一点是,在输出
“8:
后返回”
时,您正在“丢失”最后一个“a”。在这个位置上,也应该处理拖尾字符,但是您不能简单地将它们返回到那里,既不能直接返回,也不能通过解压缩返回,因为这样会导致bbbababababababababa
遗憾的是,我没有找到一个基于您的代码返回正确值的解决方案(我猜您如何将部分处理过的文本放入递归中有一些奇怪的行为,但我不确定……)
然而,我考虑了如何解决这个压缩问题,并提出了两个非递归的解决方案。也许它们可以帮助您改进解决方案。旁注:我的解决方案假设字符串格式良好,即它没有任何不匹配的括号等。
(我使用了一个重复函数,我把它放在了答案的末尾。)
第一个解决方案使用正则表达式,搜索数字和以下部分(一个字符或不包含括号的括号括起来的部分)。通过这种方式,括号和单字符解压缩从内部到外部进行处理
public static String decompressWithRegex(String s) {
if ((s == null) || (s.length() == 0)) {
return s;
}
// pattern for finding number with either bracket-enclosed, char-only part or single char
Pattern p = Pattern.compile("(\\d+)((?:[^\\d\\(\\)]{1})|(?:\\([^\\d\\(\\)]+\\)))");
String tmp = s;
Matcher m = p.matcher(tmp);
// start searching
while (m.find(0)) {
// first capture group returns count
int count = Integer.parseInt(m.group(1));
// second group is string to repeat (if it's bracket-enclosed, then remove brackets)
String what = m.group(2).replace("(", "").replace(")", "");
// build replacement part
String replacePart = repeat(what, count);
// replace it
tmp = m.replaceFirst(replacePart);
// reset matcher (source of matcher is now the new string)
m.reset(tmp);
}
return tmp;
}
第二个解决方案不使用正则表达式。相反,它对如何处理解压缩进行了一些假设:
- 任何不跟在括号内的零件后面的数字都可以直接输入 就地解压,这是先完成的
- 通过查找第一个闭合支架来处理支架封闭零件
- 然后从后面开始搜索开口括号
- 这会让你重复这个部分
- 左括号应该有一个数字,然后搜索和分析
- 现在,我们有了所有的信息,更换的部分是建立和放置在正确的地方
- 然后搜索下一个结束括号(如果有),并按上述方式处理
- 如果没有右括号,字符串将被解压缩
public static String repeat(String what, int times) {
if ((times <= 0) || (what == null) || (what.length() == 0)) {
return "";
}
StringBuilder buffer = new StringBuilder(times * what.length());
for (int i = 0; i < times; i++) {
buffer.append(what);
}
return buffer.toString();
}
公共静态字符串重复(字符串内容,整数倍){
如果((times使用一个元组作为递归的返回值,它除了提供累积字符串外还提供右括号的索引:
index 0 1 2 3 4 5 6 7 8 9 10
str 2 ( 3 b 3 ( a b ) ) a
f(0)
=> 2 * f(1)[0] add f(f(1)[1] + 1) // f(1)[1] is the closing index
f(1) => 3 * b + 3 * f(5)[0] add f(f(5)[1] + 1)
=> f(5) returns (ab,8)
f(1) => bbb + ababab add f(9) // str[9] is closing parenthesis
=> f(1) returns (bbbababab,9)
=> 2 * bbbababab add f(10)
=> bbbabababbbbabababa
JavaScript代码:
var示例='2(3b3(ab)2(cd3(fg)))ab2(gh2(xz))';
console.log(示例);
log(解压缩(示例));
函数解压缩{
//返回元组[累加器,右括号索引]
函数f(i){
累计风险=“”,
mult=“”,
curr='';
//在此级别中累积所有附加组
而(i!==s.length){
//右括号
如果(s[i]==')'){
//添加最后一次解压缩
如果(当前!=''){
累计+=自定义复制(当前、多个);
}
//退出此呼叫
返回[累计,i];
}
//字符是一个数字
如果(!isNaN(parseInt(s[i])){
//添加以前的解压缩
如果(当前!=''){
累计+=自定义复制(当前、多个);
curr='';
mult=s[i];
}否则{
mult+=s[i];
}
i++;
//性格就是性格
}否则如果(s[i]!=='('){
curr+=s[i];
i++;
//附加群
}如果(s[i]=='('){
//递归调用
[tempAccum,index]=f(i+1);
accum+=定制复制(tempAccum、mult);
mult='';
i=指数+1;
}
}
返回累计+自定义复制(当前、多个);
}
//初始化递归
返回f(0);
}
函数customReplicate(str,times){
返回新数组(次==''?1:parseInt(次))
.填充(str).连接(“”);
}
我意识到这是一个Java问题,但在用Java实现之前,我通常会编写一个小的Ruby代码来测试一个想法。如果有人感兴趣,下面是我的代码:
def decompress(str)
str.gsub!(/(\d+)([a-z])/i){$2*$1.to_i} # Replace every subtring like "3b" and "11a".
while str.include?('(') do
str.sub!(/(\d+)\(([a-z]+)\)/){$2*$1.to_i} # Replace the first inner group found
end
str
end
puts decompress("4(ab)") == "abababab"
puts decompress("11ab") == "aaaaaaaaaaab"
puts decompress("2(3b3(ab))") == "bbbabababbbbababab"
puts decompress("4(ab)a") == "ababababa"
puts decompress("2(3b3(ab))a") == "bbbabababbbbabababa"
#=> true, true, true, true, true
@jCoder在他的第一个示例中写了几乎完全相同的东西,因此无需重新发明轮子!对于2(3b3(ab))的预期输出一个是bbbababbbaba
有趣的问题。你可以使用递归下降解析器和一点BNF语法。然后你可以在大约10分钟内完成代码。好的,添加了JavaScript代码示例。很好。我真的很喜欢第一个示例。我想我理解其中的逻辑,但我的java知识不足以实际实现它。我尝试添加一个for循环,当遇到一个数字后跟一个括号时,它会“向前看”。返回括号中包含的压缩部分+接下来的内容。类似于:return repeated(直到“)”)+“compressedtext(后面的所有内容”)))。问题是,如果输入是代码中使用的字符串,则
def decompress(str)
str.gsub!(/(\d+)([a-z])/i){$2*$1.to_i} # Replace every subtring like "3b" and "11a".
while str.include?('(') do
str.sub!(/(\d+)\(([a-z]+)\)/){$2*$1.to_i} # Replace the first inner group found
end
str
end
puts decompress("4(ab)") == "abababab"
puts decompress("11ab") == "aaaaaaaaaaab"
puts decompress("2(3b3(ab))") == "bbbabababbbbababab"
puts decompress("4(ab)a") == "ababababa"
puts decompress("2(3b3(ab))a") == "bbbabababbbbabababa"
#=> true, true, true, true, true