生成字符串的所有组合的算法

时间:2012-01-23 00:07:41

标签: java algorithm combinations

我在网上找到了一个链接,显示了生成字符串的所有组合的算法:http://www.mytechinterviews.com/combinations-of-a-string

算法复制如下。

void combine(String instr, StringBuffer outstr, int index)
{
    for (int i = index; i < instr.length(); i++)
    {
        outstr.append(instr.charAt(i));
        System.out.println(outstr);
        combine(instr, outstr, i + 1);
        outstr.deleteCharAt(outstr.length() - 1);
    }
} 

combine("abc", new StringBuffer(), 0);

我不明白的是这句话:

outstr.deleteCharAt(outstr.length() - 1);

如果我删除此行,程序显然不再起作用,但为什么首先需要它?我理解递归的想法,我们改变一个初始字符并递归剩余的字符,但deleteChar行似乎在逻辑上不适合任何地方。添加outstr.deleteCharAt行的原因是什么?

10 个答案:

答案 0 :(得分:30)

计算字符串可能组合的最简单方法是......

在给定批次的N = NcR

中找到R组合的数学方法

所以我们在这里发现的是,所有可能的组合= Nc0 + Nc1 .... + Ncn = 2 Pow N

因此,对于长度为N个字符的给定单词,您将获得2个Pow N组合。

如果你用二进制代表1到(2 Pow N)整数,并将你的char放在1所在的地方,最后你会得到解决方案。

实施例

输入:ABC

解决方案:

ABC长度为3.因此可能的组合2 Pow 3 = 8

如果0 - 8用二进制表示

000 =

001 = C

010 = B

011 = BC

100 = A

101 = AC

110 = AB

111 = ABC

以上显示了所有可能的组合。

答案 1 :(得分:8)

outstr.deleteCharAt的来电通过删除outstr.append的最后一个字符来反击outstr的效果。

每个循环迭代按如下方式进行:

  1. 追加一个角色
  2. 打印结果
  3. 在级别i+1
  4. 执行递归调用
  5. 删除我们在步骤1中添加的字符

答案 2 :(得分:4)

它平衡了循环体的第一行,恢复了它在循环体顶部的位置(通过从附加的instr中删除字符)。

答案 3 :(得分:3)

非常符合逻辑。你看我们这里有一个递归算法。在位置i的每一步,我们放置一个字符串的字母,然后递归调用该函数,将另一个字母放在下一个位置。但是,当我们从递归返回时,我们需要删除我们最初放置的字符,以便我们可以将它替换为序列中的下一个可能的字符。例如:

append a on pos 0 -> a
call recursion
append a on pos 1 -> aa
call recursion
append a on pos 2 -> aaa
return from recursion
remove a from pos 2 -> aa
append b on pos 2 -> aab
return from recursion
remove b from pos 2 -> aa
append c on pos 2 -> aac
etc.

答案 4 :(得分:3)

下面的代码是生成排列和字符串组合,基本上概念是一次选择一个字符:

public class permutecombo
{
  static void initiate(String s)
  {
    permute("", s);
    System.out.println("----------------------------------------- ");
    combo("", s);
    System.out.println("----------------------------------------- ");
  }

  static void combo(String prefix, String s)
  {
    int N = s.length();

    System.out.println(prefix);

    for (int i = 0 ; i < N ; i++)
      combo(prefix + s.charAt(i), s.substring(i+1));
  }
  static void permute(String prefix, String s)
  {
    int N = s.length();

    if (N == 0)
      System.out.println(" " + prefix);

    for (int i = 0 ; i < N ; i++)
      permute(prefix + s.charAt(i), s.substring(0, i) + s.substring(i+1, N));
  }

  public static void main(String[] args)
  {
    String s = "1234";
    initiate(s);
  }
}

答案 5 :(得分:1)

outstr.deleteCharAt(outstr.length() - 1); 

表示你有

n^(n-1)/2 pairs of combinations.

迭代for循环在递归函数调用之后不会停止,因此您需要删除输出缓冲区中的最后一个char,因为您不想获取

n^n/2 pairs of combinations.

在图论中,它将是一个短路。

答案 6 :(得分:1)

我们可以使用前面提到的位概念生成字符串的所有子字符串。这是代码(用C ++编写,但你明白了): -

string s;
int n = s.size();
int num = 1<<n;
for(int i =1; i< num ; i++){ //Checks all the permutations.
    int value = i;
    int j, pos;
    for (j=1, pos=1; j < num; j<<=1, pos++) //Finds the bits that are set
        if (i & j)
            cout<<s[pos-1]; //You can print s[n-pos] to print according to bit position
    cout<<endl;        
}

例如; - 字符串s = abc

 The size is 3  . So we check from 1 to 7 ( 1<<3).
 for i = 1 ( 001 ) , the first bit is set, so a is printed.
 for i = 2 ( 010 ) , the second bit is set, so b is printed.
 for i = 3 ( 011 ) , the first and second bit are set, so ab is printed.
 .
 .
 .
 for i = 7 ( 111 ) , all three bits are set, so abc is printed.

答案 7 :(得分:1)

这是C ++代码,在OP的问题中没有棘手的回溯步骤。

#include <iostream>
#include <string>
using namespace std;
static const string in("abc");
void combine(int i, string out)
{
    if (i==in.size()) {
        cout << out << endl;
        return;
    }
    combine(i+1, out);
    combine(i+1, out+in[i]);
}

int main()
{
    combine(0, "");
    return 0;
}

我希望这能更好地体现组合精神。

答案 8 :(得分:0)

header("Content-Type: text/json");

答案 9 :(得分:0)

import com.google.common.collect.Lists;

import java.util.List;

public class Combinations {
    public static String[] getCombinations(final String input) {
        final List<String> combinations = Lists.newArrayList();
        getCombinations(input.toCharArray(), combinations, 0, "");
        return combinations.toArray(new String[0]);
    }

    private static void getCombinations(final char[] input, final List<String> combinations, final int index, final String combination) {
        if (index == input.length) {
            combinations.add(combination);
            return;
        }
        getCombinations(input, combinations, index + 1, combination + String.valueOf(input[index]));
        getCombinations(input, combinations, index + 1, combination);
    }

}

相应测试:

import org.hamcrest.Matchers;
import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;

public class CombinationsTest {
    @Test
    public void testCombinations() {
        verify(Combinations.getCombinations(""), "");
        verify(Combinations.getCombinations("a"), "a", "");
        verify(Combinations.getCombinations("ab"), "ab", "a", "b", "");
        verify(Combinations.getCombinations("abc"), "abc", "ab", "ac", "a", "bc", "b", "c", "");
        verify(Combinations.getCombinations("abcd"),
                "abcd", "abc", "abd", "ab", "acd", "ac", "ad", "a", "bcd", "bc", "bd", "b", "cd", "c", "d", "");
    }

    private void verify(final String[] actual, final String... expected) {
        assertThat(actual, Matchers.equalTo(expected));
    }
}