web-dev-qa-db-ja.com

文字列のすべての組み合わせを生成するアルゴリズム

文字列のすべての組み合わせを生成するアルゴリズムを示すリンクをオンラインで見つけました: 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行を追加した理由は何ですか?

16
john

outstr.deleteCharAtの呼び出しは、outstrの最後の文字を削除することにより、outstr.appendの影響を打ち消します。

各ループの反復は次のように進行します。

  1. 文字を追加する
  2. 結果を印刷する
  3. レベルi+1で再帰呼び出しを実行する
  4. 手順1で追加した文字を削除します
8
dasblinkenlight

文字列の可能な組み合わせを計算する最も簡単な方法はここにあります...

数学的に与えられたロットのN = NcRでRの組み合わせを見つける

したがって、ここで見つけているのは、すべての可能な組み合わせ= Nc0 + Nc1 .... + Ncn = 2 Pow N

したがって、指定された長さNの文字に対して2つのPow Nの組み合わせが得られます。

1から(2 Pow N)の整数を2進数で表し、1が存在する場所にcharを配置すると、最終的に解が得られます。

例:

入力:ABC

解決 :

ABCの長さは3なので、可能な組み合わせは2 Pow 3 = 8

0から8がバイナリーで表される場合

000 =

001 = C

010 = B

011 = BC

100 = A

101 = AC

110 = AB

111 = ABC

可能なすべての組み合わせが上記に示されています。

32
Sunil

ループ本体の最初の行のバランスを取り、(追加されたinstrから文字を削除することにより)outstrをループ本体の先頭にあったものに戻します。

4
Scott Hunter

前述のビットの概念を使用して、文字列のすべてのサブ文字列を生成できます。これを行うためのコードは次のとおりです(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.
3
user2125722

以下のコードは、文字列の順列と組み合わせを生成するものです。基本的には、一度に1文字を選択するという概念です。

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);
  }
}
3
nmd

それは非常に論理的に適合します。ここにあるのは再帰アルゴリズムです。位置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.
3
Tudor

OPの質問にトリッキーなバックトラッキング手順がないC++コードを次に示します。

#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;
}

これが組み合わせの精神をよりよく捉えることを願っています。

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

あなたが持っていることを意味します

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

反復forループは再帰関数呼び出しの後に停止しないため、取得したくないので、出力バッファーの最後の文字を削除する必要があります

n^n/2 pairs of combinations.

グラフ理論では、これは短絡になります。

1
Gigamegs
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));
    }
}
0
Coder93
// IF YOU NEED REPEATITION USE ARRAYLIST INSTEAD OF SET!!

import Java.util.*;
public class Permutation {

    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        System.out.println("ENTER A STRING");
        Set<String> se=find(in.nextLine());
        System.out.println((se));
    }
    public static Set<String> find(String s)
    {
        Set<String> ss=new HashSet<String>();
        if(s==null)
        {
            return null;
        }
        if(s.length()==0)
        {
            ss.add("");
        }
        else
        {
            char c=s.charAt(0);
            String st=s.substring(1);
            Set<String> qq=find(st);
            for(String str:qq)
            {
                for(int i=0;i<=str.length();i++)
                {
                    ss.add(comb(str,c,i));
                }
            }
        }
        return ss;

    }
    public static String comb(String s,char c,int i)
    {
        String start=s.substring(0,i);
        String end=s.substring(i);
        return start+c+end;
    }

}


// IF YOU NEED REPEATITION USE ARRAYLIST INSTEAD OF SET!!