【问题标题】:Find all the combination of substrings that add up to the given string查找加起来为给定字符串的所有子字符串组合
【发布时间】:2015-07-22 01:41:42
【问题描述】:

我正在尝试创建一个数据结构,它包含所有可能的子字符串组合,这些组合加起来是原始字符串。例如,如果字符串为"java",则有效结果为"j", "ava""ja", "v", "a",则无效结果为"ja", "a""a", "jav"

我很容易找到所有可能的子字符串

    String string = "java";
    List<String> substrings = new ArrayList<>();
    for( int c = 0 ; c < string.length() ; c++ )
    {
        for( int i = 1 ; i <= string.length() - c ; i++ )
        {
            String sub = string.substring(c, c+i);
            substrings.add(sub);
        }
    }
    System.out.println(substrings);

现在我正在尝试构建一个仅包含有效子字符串的结构。但它几乎没有那么容易。我陷入了一个非常丑陋的代码的迷雾中,摆弄着索引,而且还没有完成,很可能完全走错了路。有什么提示吗?

【问题讨论】:

  • List&lt;String&gt; 保存有效子字符串有什么问题?或者你真的想找到一种数据类型来有效地只表示有效的子字符串?
  • 假设您的意思是 proper 子字符串,因为空字符串也是子字符串。
  • 不不,这与结构无关,我正在构建一个Set&lt;List&lt;String&gt;&gt;,但它甚至可能是一个二维数组,我的问题是逻辑,如何找到合适的子字符串
  • @aioobe 是的,正确的不是空子字符串
  • 你应该尝试递归:取第一个字符,然后分割或不分割,然后分割其余的。

标签: java string algorithm recursion iteration


【解决方案1】:

这是一种方法:

static List<List<String>> substrings(String input) {

    // Base case: There's only one way to split up a single character
    // string, and that is ["x"] where x is the character.
    if (input.length() == 1)
        return Collections.singletonList(Collections.singletonList(input));

    // To hold the result
    List<List<String>> result = new ArrayList<>();

    // Recurse (since you tagged the question with recursion ;)
    for (List<String> subresult : substrings(input.substring(1))) {

        // Case: Don't split
        List<String> l2 = new ArrayList<>(subresult);
        l2.set(0, input.charAt(0) + l2.get(0));
        result.add(l2);

        // Case: Split
        List<String> l = new ArrayList<>(subresult);
        l.add(0, input.substring(0, 1));
        result.add(l);
    }

    return result;
}

输出:

[java]
[j, ava]
[ja, va]
[j, a, va]
[jav, a]
[j, av, a]
[ja, v, a]
[j, a, v, a]

【讨论】:

  • 我印象深刻,因为我需要更多字符来发表评论,所以我会重复一遍,我印象深刻
  • 感谢发帖。很棒的面试问题练习。我发现这类问题很难快速解决。
  • 对我来说这绝对是困难的,额外的技巧是它看起来很容易,直到你真正尝试它,再次感谢。
  • 你太棒了!!!我可以请得到这个的javascript版本。当我尝试在 javascript 中执行此操作时,我的浏览器挂起。
  • @kashyaphp jsfiddle.net/u49qu6ut 注意对于长度为 n 的字符串有 2^(n-1) 个结果,所以不要尝试输入太大。
【解决方案2】:

这似乎是找到字符串长度的compositions,并使用这些组合来制作子字符串的问题。所以有 2^n-1 个数字 n 的组合,这对于长字符串来说可能会有点耗时......

【讨论】:

    【解决方案3】:

    可能有人想要另一种非递归且不占用内存来保存列表的解决方案:

    public static List<List<String>> substrings(final String input) {
        if(input.isEmpty())
            return Collections.emptyList();
        final int size = 1 << (input.length()-1); 
        return new AbstractList<List<String>>() {
    
            @Override
            public List<String> get(int index) {
                List<String> entry = new ArrayList<>();
                int last = 0;
                while(true) {
                    int next = Integer.numberOfTrailingZeros(index >> last)+last+1;
                    if(next == last+33)
                        break;
                    entry.add(input.substring(last, next));
                    last = next;
                }
                entry.add(input.substring(last));
                return entry;
            }
    
            @Override
            public int size() {
                return size;
            } 
        };
    }
    
    public static void main(String[] args) {
        System.out.println(substrings("java"));
    }
    

    输出:

    [[java], [j, ava], [ja, va], [j, a, va], [jav, a], [j, av, a], [ja, v, a], [j, a, v, a]]
    

    它只是根据它的索引计算下一个组合。

    【讨论】:

    • 我喜欢这个解决方案!
    • 使用列表数组而不是列表列表更有意义。数组索引将对应于将字符串划分为子字符串的方式。例如,元素 6 对应于二进制 110,它在第一个空格中不放置逗号,在第二个位置放置一个逗号,在第三个位置放置一个逗号:[ja,v,a]。
    • @EdwardDoolittle,对于列表数组,有必要将所有组合实际存储到内存中,因为 Java 中不能有一个动态计算的数组。我做了同样的事情,但对于列表索引。
    【解决方案4】:

    以防万一有人会在 python 中寻找相同的算法,这里是 Python 中的实现:

    from itertools import combinations
    
    def compositions(s):
        n = len(s)
        for k in range(n):
            for c in combinations(range(1, n), k):
                yield tuple(s[i:j] for i, j in zip((0,) + c, c + (n,)))
    

    工作原理示例:

    >>> for x in compositions('abcd'):
    ...     print(x)
    ('abcd',)
    ('a', 'bcd')
    ('ab', 'cd')
    ('abc', 'd')
    ('a', 'b', 'cd')
    ('a', 'bc', 'd')
    ('ab', 'c', 'd')
    ('a', 'b', 'c', 'd')
    

    稍作修改,您就可以按不同的顺序生成组合:

    def compositions(s):
        n = len(s)
        for k in range(n):
            for c in itertools.combinations(range(n - 1, 0, -1), k):
                yield tuple(s[i:j] for i, j in zip((0,) + c[::-1], c[::-1] + (n,)))
    

    它会给你这个:

    >>> for x in compositions('abcd'):
    ...     print(x)
    ('abcd',)
    ('abc', 'd')
    ('ab', 'cd')
    ('a', 'bcd')
    ('ab', 'c', 'd')
    ('a', 'bc', 'd')
    ('a', 'b', 'cd')
    ('a', 'b', 'c', 'd')
    

    再加上一点,你只能生成指定数量的分割:

    def compositions(s, r=None):
        n = len(s)
        r = range(n) if r is None else [r - 1]
        for k in r:
            for c in itertools.combinations(range(n - 1, 0, -1), k):
                yield tuple(s[i:j] for i, j in zip((0,) + c[::-1], c[::-1] + (n,)))
    
    >>> for x in compositions('abcd', 3):
    ...     print(x)
    ('ab', 'c', 'd')
    ('a', 'bc', 'd')
    ('a', 'b', 'cd')
    

    【讨论】:

      【解决方案5】:

      一种不同的递归解决方案,只是添加到列表结果中

      static List<List<String>> substrings(String input) {
          List<List<String>> result = new ArrayList<>();
          if (input.length() == 1) {
              result.add(Arrays.asList(new String[]{input}));
          }
          else {
              //iterate j, ja, jav, jav
              for (int i = 0; i < input.length()-1; i++ ) {
                  String root = input.substring(0,i+1);
                  String leaf = input.substring(i+1);
                  for( List<String> strings: substrings(leaf) ) {
                      ArrayList<String> current = new ArrayList<String>();
                      current.add(root);
                      current.addAll(strings);
                      result.add(current);
                  }
              }
              //adds the whole string as one of the leaves (ie. java, ava, va, a)
              result.add(Arrays.asList(new String[]{input}));
          }
          return result;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-20
        • 2012-05-04
        • 2016-04-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-29
        • 2017-05-26
        相关资源
        最近更新 更多