【问题标题】:Optimal algorithm for this string decompression此字符串解压缩的最佳算法
【发布时间】:2020-07-22 17:24:08
【问题描述】:

我一直在按照 google 的开发技术指南进行练习。它被称为压缩和解压缩,您可以查看以下链接以获取问题的描述Challenge Description

这是我的解决方案代码:

public static String decompressV2 (String string, int start, int times) {
    String result = "";
    for (int i = 0; i < times; i++) {
        inner:
        {
            for (int j = start; j < string.length(); j++) {
                if (isNumeric(string.substring(j, j + 1))) {
                    String num = string.substring(j, j + 1);
                    int times2 = Integer.parseInt(num);
                    String temp = decompressV2(string, j + 2, times2);
                    result = result + temp;
                    int next_j = find_next(string, j + 2);
                    j = next_j;
                    continue;
                }
                if (string.substring(j, j + 1).equals("]")) {  // Si es un bracket cerrado
                    break inner;
                }

                result = result + string.substring(j,j+1);
            }
        }
    }
    return result;
}

public static int find_next(String string, int start) {
    int count = 0;
    for (int i = start; i < string.length(); i++) {
        if (string.substring(i, i+1).equals("[")) {
            count= count + 1;
        }
        if (string.substring(i, i +1).equals("]") && count> 0) {
            count = count- 1;
            continue;
        }
        if (string.substring(i, i +1).equals("]") && count== 0) {
            return i;
        }
    }
    return -111111;
}

我将稍微解释一下我的方法的内部工作原理。这是一个基本的解决方案,涉及使用简单的递归和循环。

那么,让我们从简单的解压开始吧:

DevTech.decompressV2("2[3[a]b]", 0, 1);

如您所见,0 表示它必须遍历索引 0 处的字符串,而 1 表示该字符串必须只计算一次:1[ 2[3[ a]b] ]

这里的核心是,每次遇到一个数字时,你都会再次(递归地)调用算法并继续到括号内的字符串结束的地方,这就是 find_next 函数。

当它找到一个右括号时,内部循环中断,这就是我选择制作停止标志的方式。

我认为这将是算法背后的主要思想,如果您仔细阅读代码,您将获得全貌。

以下是我对编写解决方案的方式的一些担忧:

  • 我找不到更干净的解决方案来告诉算法如果找到数字则下一步。所以我用 find_next 函数对它进行了硬编码。有没有办法在 decompress func 中做到这一点更干净?
  • 关于性能,当你有一个大于 1 的数字在一个括号的乞求时,再次做同样的事情会浪费很多时间。
  • 我比较喜欢编程,所以也许这段代码也需要改进,不是在想法上,而是在它的编写方式上。所以很感激能得到一些建议。
  • 这是我想出的方法,但我相信还有更多,我想不出任何人,但如果你能说出你的想法,那就太好了。
  • 在描述中,它告诉您在开发解决方案时应注意的一些事项。它们是:处理非重复的字符串,处理内部的重复,不要两次做同样的工作,不要复制太多。我的方法是否涵盖了这些?
  • 最后一点是关于tets case,我知道在开发解决方案时信心非常重要,而给算法信心的最好方法是测试用例。我尝试了一些,它们都按预期工作。但是你推荐什么技术来开发测试用例。有软件吗?

所以这就是所有人,我是社区的新手,所以我愿意接受有关如何提高问题质量的建议。干杯!

【问题讨论】:

标签: string algorithm performance recursion time-complexity


【解决方案1】:

您的解决方案涉及大量字符串复制,这确实会减慢速度。您应该将StringBuilder 传递给每个调用,并将append 子字符串传递给它,而不是返回您连接的字符串。

这意味着您可以使用返回值来指示继续扫描的位置。

您还不止一次地解析源字符串的重复部分。

我的解决方案如下所示:

public static String decompress(String src)
{
    StringBuilder dest = new StringBuilder();
    _decomp2(dest, src, 0);
    return dest.toString();
}

private static int _decomp2(StringBuilder dest, String src, int pos)
{
    int num=0;
    while(pos < src.length()) {
        char c = src.charAt(pos++);
        if (c == ']') {
            break;
        }
        if (c>='0' && c<='9') {
            num = num*10 + (c-'0');
        } else if (c=='[') {
            int startlen = dest.length();
            pos = _decomp2(dest, src, pos);
            if (num<1) {
                // 0 repetitions -- delete it
                dest.setLength(startlen);
            } else {
                // copy output num-1 times
                int copyEnd = startlen + (num-1) * (dest.length()-startlen);
                for (int i=startlen; i<copyEnd; ++i) {
                    dest.append(dest.charAt(i));
                }
            }
            num=0;
        } else {
            // regular char
            dest.append(c);
            num=0;
        }
    }
    return pos;
}

【讨论】:

  • 不错。我喜欢更新数字而不是累积字符串并进行转换的想法。
【解决方案2】:

我会尝试返回一个元组,该元组还包含应该继续解压的下一个索引。然后我们可以进行递归,将当前部分与当前递归深度中的块的其余部分连接起来。

这是 JavaScript 代码。封装反映规则的操作顺序需要一些思考。

function f(s, i=0){
  if (i == s.length)
    return ['', i];
    
  // We might start with a multiplier
  let m = '';
  while (!isNaN(s[i]))
    m = m + s[i++];

  // If we have a multiplier, we'll
  // also have a nested expression
  if (s[i] == '['){
    let result = '';
    const [word, nextIdx] = f(s, i + 1);
    for (let j=0; j<Number(m); j++)
      result = result + word;
    const [rest, end] = f(s, nextIdx);
    return [result + rest, end]
  }
  
  // Otherwise, we may have a word,
  let word = '';
  while (isNaN(s[i]) && s[i] != ']' && i < s.length)
    word = word + s[i++];

  // followed by either the end of an expression
  // or another multiplier
  const [rest, end] = s[i] == ']' ? ['', i + 1] : f(s, i);
  return [word + rest, end];
}

var strs = [
 '2[3[a]b]',
  '10[a]',
  '3[abc]4[ab]c',
  '2[2[a]g2[r]]'
];

for (const s of strs){
  console.log(s);
  console.log(JSON.stringify(f(s)));
  console.log('');
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    相关资源
    最近更新 更多