Как найти перестановку k заданной длины?

Как я могу найти перестановки k заданной длины?

Например:

В слове cat 3 буквы: Как мне найти все перестановки 2 в слове cat. Результат должен быть: ac, at, ca, ac и т.д...


Это не проблема домашнего задания. Можно использовать любой язык, но предпочтительнее: C/C++ или C#. Я знаю, как создать рекурсию для размера LENGTH, но не для нестандартного размера.


person Y_Y    schedule 28.02.2010    source источник
comment
Какой-то конкретный язык?   -  person Ignacio Vazquez-Abrams    schedule 28.02.2010
comment
Звучит как проблема с домашним заданием.   -  person    schedule 28.02.2010
comment
Нет... Это не домашнее задание, и можно использовать любой язык, но предпочтительнее: C/C++ или C#.   -  person Y_Y    schedule 28.02.2010
comment
Я знаю, как создать рекурсию для размера LENGHT, но не для нестандартного размера.   -  person Y_Y    schedule 28.02.2010
comment
Должны ли разделы быть лексикографическими?   -  person pestilence669    schedule 28.02.2010
comment
Могут ли персонажи повторяться? Я предполагаю, что они могут быть...   -  person    schedule 28.02.2010
comment
Дубликат: stackoverflow.com/questions/1663949/, stackoverflow.com/questions/127704/   -  person kennytm    schedule 28.02.2010
comment
127704 не является обманом (речь идет о комбинациях, а не перестановках), а 1663949, похоже, не имеет дело с повторяющимися символами, хотя я ожидаю, что об этом задавали раньше.   -  person    schedule 28.02.2010
comment
@Moron: Кто сказал, что символы могут повторяться? Никакого aa в результатах быть не должно.   -  person kennytm    schedule 28.02.2010
comment
@KennyTM: Нет. Я не это имел в виду. Поскольку он, кажется, хочет английские слова, банан является допустимым вводом, поэтому теперь вы не должны дублировать aa в выводе (что будет делать стандартный алгоритм).   -  person    schedule 28.02.2010


Ответы (5)


Вот один из них на C#, который должен работать даже с повторяющимися символами. Например, для «банана» для перестановок длины 2 это дает:

ba bn ab aa an nb na nn

Основная идея состоит в том, чтобы исправить первый символ, затем сформировать все перестановки длины k-1, а затем добавить символ к этим перестановкам длины k-1. Чтобы справиться с повторяющимися символами, мы отслеживаем количество оставшихся символов (то есть тех, которые можно использовать для подперестановок).

Не образцовый код, но должен дать вам представление. (Если вы найдете ошибки, дайте мне знать, и я могу исправить).

static List<string> Permutations(Dictionary<char, int> input, int length) {
    List<string> permutations = new List<string>();

    List<char> chars = new List<char>(input.Keys);

    // Base case.
    if (length == 0) {
        permutations.Add(string.Empty);
        return permutations;
    }

    foreach (char c in chars) {

        // There are instances of this character left to use.
        if (input[c] > 0) {

            // Use one instance up.
            input[c]--;

            // Find sub-permutations of length length -1.
            List<string> subpermutations = Permutations(input, length - 1);

            // Give back the instance.
            input[c]++;

            foreach (string s in subpermutations) {

                // Prepend the character to be the first character.
                permutations.Add(s.Insert(0,new string(c,1)));

            }
        }
    }

    return permutations;
}

И вот полная программа, которая у меня есть, чтобы использовать ее:

using System;
using System.Collections.Generic;

namespace StackOverflow {

    class Program {
        static void Main(string[] args) {
            List<string> p = Permutations("abracadabra", 3);
            foreach (string s in p) {
                Console.WriteLine(s);
            }
        }

        static List<string> Permutations(string s, int length) {
            Dictionary<char, int> input = new Dictionary<char, int>();
            foreach (char c in s) {
                if (input.ContainsKey(c)) {
                    input[c]++;
                } else {
                    input[c] = 1;
                }
            }
            return Permutations(input, length);
        }

        static List<string> Permutations(Dictionary<char, int> input, 
                                                          int length) {
            List<string> permutations = new List<string>();

            List<char> chars = new List<char>(input.Keys);
            if (length == 0) {
                permutations.Add(string.Empty);
                return permutations;
            }

            foreach (char c in chars) {
                if (input[c] > 0) {
                    input[c]--;
                    List<string> subpermutations = Permutations(input, 
                                                                length - 1);
                    input[c]++;

                    foreach (string s in subpermutations) {
                        permutations.Add(s.Insert(0,new string(c,1)));
                    }
                }
            }

            return permutations;
        }
    }
}
person Community    schedule 28.02.2010
comment
@Y_Y: Рад, что помог! Было интересно пересмотреть это спустя долгое время. - person ; 01.03.2010

Что не так с рекурсивным решением и передачей дополнительного параметра (глубины), чтобы рекурсивная функция немедленно возвращалась для глубины > n.

person ta.speot.is    schedule 28.02.2010

Не самый эффективный, но работает:

public class permutation
{
    public static List<string> getPermutations(int n, string word)
    {
        List<string> tmpPermutation = new List<string>();
        if (string.IsNullOrEmpty(word) || n <= 0)
        {
            tmpPermutation.Add("");
        }
        else
        {
            for (int i = 0; i < word.Length; i++)
            {
                string tmpWord = word.Remove(i, 1);
                foreach (var item in getPermutations(n - 1, tmpWord))
                {
                    tmpPermutation.Add(word[i] + item);
                }
            }
        }
        return tmpPermutation;
    }
}
person Francisco    schedule 28.02.2010

Если не ошибаюсь, эту проблему можно решить и комбинадикой, как на http://en.wikipedia.org/wiki/Combinadic/, там тоже есть эталонные реализации.

Я использовал решение Java (http://docs.google.com/Doc?id=ddd8c4hm_5fkdr3b/) для создания всех возможных троек из последовательности чисел, это не должно быть исключением.

Мне не хватает средств, чтобы объяснить математику, стоящую за этим, но, насколько я понимаю, это наименее сложный способ перебора всех возможных вариантов nCr (например, 3C2 для вашего примера с котом) в коллекции.

person Derek Mortimer    schedule 28.02.2010

Сначала найдите возможные подмножества вашего массива. Вы можете сделать это рекурсивным способом, который обсуждался в разделе Итерация по подмножествам любого размера

Во-вторых, вычислите перестановки каждого подмножества с помощью алгоритма STL next_permutation.

Я не реализовал это, но я думаю, что это должно работать.

person Christian Ammer    schedule 28.02.2010