根据任意字符串和长度生成字符组合 - 类似于排列

时间:2013-12-04 07:21:45

标签: algorithm delphi

之前在其他语言中询问过这个问题,但在搜索SO之后没有问过delphi。 看到这个问题:How to Generate Permutations With Repeated Characters和这个问题:Generate all combinations of arbitrary alphabet up to arbitrary length和这一个:How to generate combination of fix length strings using a set of characters? 所以问题不是新问题,但我很难将其中任何一个转换为delphi。

我要做的是生成包含重复字符的组合,例如: 如果我们有一个字符串(由用户指定):ABC,我们想要生成三个字符的长度(也是用户指定的长度),我会得到: AAA AAB AAC ABA ABB ABC ACA ACB ACC BAA BAB BAC etc...

这段代码似乎是用C ++编写的:

int N_LETTERS = 4;
char alphabet[] = {'a', 'b', 'c', 'd'};

std::vector<std::string> get_all_words(int length)
{
  std::vector<int> index(length, 0);
  std::vector<std::string> words;

  while(true)
  {
    std::string word(length);
    for (int i = 0; i < length; ++i)
      word[i] = alphabet[index[i]];
    words.push_back(word);

for (int i = length-1; ; --i)
{ 
  if (i < 0) return words;
  index[i]++;
  if (index[i] == N_LETTERS)
    index[i] = 0;
  else
    break;
    }
  }
}

这似乎也是这样:

    #include <iostream>
    #include <string>
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    using namespace std;

void displayPermutation(string permutation[], int length){
    int i;
    for (i=0;i<length;i++){
        cout<<permutation[i];
    }
    cout << endl;
}

void getPermutations(string operatorBank[], int operatorCount, 
        string permutation[],int permutationLength, int curIndex){
    int i;
    //stop recursion condition
    if(curIndex == permutationLength){
        displayPermutation(permutation,permutationLength);
    }
    else{
        for(i = 0; i < operatorCount; i++){
            permutation[curIndex] = operatorBank[i];
            getPermutations(operatorBank,operatorCount,permutation,
                permutationLength,curIndex+1);
        }
    }
}

int main ()
   {
       int operatorCount = 4;
       int permutationLength = 3;
       string operatorBank[] = {"+","-","*","/"};
       string permutation[] = {"","","",""}; //empty string
       int curIndex = 0;
       getPermutations(operatorBank,operatorCount,permutation,
                               permutationLength,curIndex);
   return 0;
   }
在这里找到最接近我想要的delphi,但不允许AAA例如: http://www.swissdelphicenter.ch/torry/showcode.php?id=1032

如果您猜测,这不是家庭作业。没有其他动机,只是学习。

EDIT3: 从问题中删除了所有不相关的代码,以便其他人更容易阅读并获得以下答案。在2个不同方法的答案下查看完成此操作:一个使用递归,另一个使用计数器函数。

3 个答案:

答案 0 :(得分:4)

你展示的例子使这比必要的复杂得多,至少是IMO。

你真正关注的是一个3位数的基数3。您可以从0到3 3 = 27计数,然后将每个数字转换为基数3(使用'A','B'和'C'作为数字而不是'0','1 '和'2')。

在C ++中,转换看起来像这样:

std::string cvt(int in) {
    static const int base = 3;
    static const int digits = 3;
    std::string ret;

    for (int i = 0; i<digits; i++) {
        ret.push_back('A' + in % base);
        in /= base;
    }
    return std::string(ret.rbegin(), ret.rend());
}

随着转换的到位,生成所有组合变得非常简单:

for (int i = 0; i < 27; i++)
    std::cout << cvt(i) << "\t";

我认为将其转换为Delphi应该只是简单的机械 - 分配从=变为:=%变为mod,整数除法变为divfor循环更改为for i = 0 to 27 do,依此类推。最繁琐(但最终非常简单)的部分可能是处理这样一个事实,即在C ++中,char只是一个小整数类型,你可以在其上进行正常的整数运算。至少如果内存服务,在Pascal(或类似Delphi的衍生物)中,你需要ord从一个字符转换为一个序数,并chr从序数转换回字符。因此'A' + in % base;会更像chr(ord('A') + in mod base);

就像我说的那样,似乎几乎整个翻译几乎完全是机械的,不需要对基本算法的工作方式进行真正的改变,或类似的事情。

答案 1 :(得分:2)

不完全遵循您的输出序列,但遵循类似于二进制数加起来的方式的序列......

0001 
0010 
0011 
0100 
...

这个想法很简单:数组中的循环索引值指示在相应位置使用哪个字符来组成输出组合字符串。不需要递归。

NextCombination更新索引数组,以便定义下一个组合,只要不形成所有组合,它就返回true。返回全0时为假。

DefineCombinations接受带有要使用的字符的字符串(例如'ABC')和组合字符串的大小(例如:3):这会将以下序列添加到备忘录中:

AAA, AAB, AAC, ABA, ABB, ABC, ACA, ACB, ACC, BAA, BAB, BAC, BBA, BBB, BBC, BCA, BCB, BCC, CAA, CAB, CAC, CBA, CBB, CBC, CCA, CCB, CCC

根据需要调整。

function TForm1.NextCombination(var aIndices: array of Integer; const MaxValue: Integer): Boolean;

var Index : Integer;

begin
  Result:=False;
  Index:=High(aIndices);

  while not(Result) and (Index >= Low(aIndices)) do
  begin
    if (aIndices[Index] < MaxValue) then
    begin
      { inc current index }
      aIndices[Index]:=aIndices[Index] + 1;
      Result:=True;
    end
    else
    begin
      { reset current index, process next }
      aIndices[Index]:=0;
      Dec(Index);
    end;
  end;
end;

procedure TForm1.DefineCombinations(const Chars: String; const Size: Integer);

var aIndices     : array of Integer;

    Index        : Integer;
    sData        : String;

begin
     try
        SetLength(sData, Size);
        SetLength(aIndices, Size);

        repeat
           for Index:=Low(aIndices) to High(aIndices) do
             sData[Index + 1]:=Chars[aIndices[Index] + 1];

           memo1.Lines.Add(sData);
        until not(NextCombination(aIndices, Length(Chars) - 1));

     finally
        SetLength(aIndices, 0);
        SetLength(sData, 0);
     end;
end;

如果我错过了原始问题,请告诉我。

答案 2 :(得分:0)

这是通过递归完成的(感谢这篇文章接受的回答:How to Generate Permutations With Repeated Characters

program Combinations;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

procedure displayPermutation(const permutation : array of char; ilength: integer);
var
  i: integer;
begin
  for i := 0 to ilength - 1 do
    begin
      if i mod iLength = 0 then
        writeln('')
      else write(permutation[i]);
    end;
end;

procedure getPermutations(const operatorBank: array of char; operatorCount: integer;
            permutation: array of char; permutationLength, curIndex: integer);
var
  i : integer;
begin
  //stop recursion condition
  if(curIndex = permutationLength)then
    displayPermutation(permutation, permutationLength)
  else
    for i := 0 to operatorCount - 1 do
      begin
        permutation[curIndex] := operatorBank[i];
        getPermutations(operatorBank,operatorCount,permutation,
                     permutationLength,curIndex+1);
      end;
 end;

var
  operatorBank,permutation : array of char;
  i, permutationLength, curIndex, operatorCount: integer;
  Q, S : String;
begin
  try

    Q  := ' ';
    S  := ' ';
    while (Q <> '') and (S <> '') do
      begin
        Writeln('');
        Write('P(N,R)  N=? : ');
        ReadLn(Q);

        operatorCount := Length(Q);
        setLength(operatorBank,operatorCount);


        for i  := 0 to operatorCount - 1 do
          operatorBank[i] := Q[i+1];

        Write('P(N,R)  R=? : ');
        ReadLn(S);
        if S <> '' then permutationLength := StrToInt(S) + 1;

        SetLength(permutation,operatorCount);
        curIndex := 0;
        Writeln('');

        getPermutations(operatorBank, operatorCount, permutation,
                      permutationLength, curIndex );
      end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.