Найти кратчайший текст, содержащий все комбинации данной строки

1

С учетом строки найдите другую строку, содержащую все комбинации входной строки.

Пример:

Если входная строка = "23", то ее комбинации будут ["22", "23", "32", "33"],

Одна из строк, содержащих все вышеперечисленные комбинации, будет "22233233", но это не было бы самым коротким. Самый короткий - "22332".

Алгоритм должен быть достаточно общим для работы с входной строкой любого размера. (Предположим, что вход не слишком велик, и выход останется под нормальным размером int/string/jvm и т.д. Также предположим, что входная строка будет иметь буквенно-цифровые символы только на английском языке)

Я пробовал следующий алгоритм, но он, похоже, не работает:

1) Найдите все комбинации строки = ["22", "23", "32", "33"]

2) Создайте префиксную карту [2: {22, 23}, 3: {32, 33}]

3) Начните с любой комбинации и суффикса поиска в префиксной карте.

Пример: Начните с 22, его суффикс равен 2 Из префикс-карты значения, соответствующие 2, равны 22 и 23. Выберите одно из слов здесь, которое не является текущим выбранным словом, поэтому оно даст 23

4) Добавить выбранный суффикс слова в текущую строку (это дает 223)

5) Повторите. Итак, я получу 223 суффикса = 3 Из префиксной карты, 3: {32, 33} Выберите любой, скажем, 32 Присоединитесь к текущей строке, чтобы получить 2232

6) Если ничего другого не найдено, добавьте текущую строку. Это дает 223233

Однако ответ должен быть 22332, поскольку это самый короткий.

Вот полный код, который я написал до сих пор:

public class TextContainingAllPermutations
{
    static String input = "ABC";

    public static void main (String args[])
    {
        int suffixLen = input.length()-1;
        Set<String> combinations = getCombinations();
        while (suffixLen > 0 && combinations.size() > 1)
        {
            Map<String, List<String>> suffixToWords = getPrefixMap(combinations, suffixLen);
            String someWordsString = combinations.iterator().next();
            combinations.remove(someWordsString);
            Set<String> combinations2 = new HashSet<String>();

            while (combinations.size() > 0)
            {
                String suffix = someWordsString.substring(someWordsString.length()-suffixLen);
                List<String> words = suffixToWords.get(suffix);
                if (words == null || words.size()==0)
                {
                    combinations2.add(someWordsString);
                    System.out.println (someWordsString);
                    if (combinations.size() == 0)
                        break;
                    someWordsString = combinations.iterator().next();
                    combinations.remove(someWordsString);
                }
                else
                {
                    String w = words.get(words.size()-1);
                    words.remove(words.size()-1);
                    combinations.remove(w);
                    if (someWordsString.indexOf(w) == -1)
                        someWordsString += w.charAt(w.length()-1); // append last char
                }
            }
            combinations2.add(someWordsString);
            System.out.println (someWordsString);
            combinations = combinations2;
            suffixLen--;
        }
    }

    private static Map<String, List<String>> getPrefixMap(Set<String> combinations, int suffixLen)
    {
        Map<String, List<String>> suffixToWords = new HashMap<String, List<String>>();
        for (String s: combinations)
        {
            String suffix = s.substring(0,suffixLen);
            if (!suffixToWords.containsKey(suffix))
            {
                suffixToWords.put(suffix, new ArrayList<String>());
            }
            suffixToWords.get(suffix).add(s);
        }
        return suffixToWords;
    }

    static Set<String> getCombinations()
    {
        char[] inputChars = input.toCharArray();
        int N = (int)Math.pow(input.length(), input.length());
        Set<String> combinations = new HashSet<String>(N);
        for (int i=0; i<N; i++)
        {
            char[] binary = padZeroes(Integer.toString(i, input.length())).toCharArray();

            String combination = "";
            for (int j=0; j<inputChars.length; j++)
            {
                char c = binary[j];
                int index = c - '0';
                char inputChar = inputChars[index];
                combination = inputChar + combination;
            }

            System.out.println (new String(binary) + " = " + combination);
            combinations.add(combination);
        }
        return combinations;
    }

    private static String padZeroes(String s)
    {
        int j = input.length()-s.length();
        for (int i=0; i<j; i++)
            s = '0' + s;
        return s;
    }
}

Это не проблема домашних заданий.

  • 0
    почему не 223323?
  • 0
    Что вы определили, будет или не будет работать?
Показать ещё 6 комментариев
Теги:
algorithm

1 ответ

5

То, что вы ищете, - это в основном последовательность Де Бройна. Последовательность B(k,n) Брейна B(k,n) является циклической последовательностью, которая содержит все возможные подпоследовательности длины n из множества k символов, каждый из которых появляется ровно один раз. Длина последовательности точно равна k n.

Минимальная нециклическая последовательность может быть получена путем разбиения цикла в любой точке и последующего копирования первых n-1 символов в конец, создавая последовательность длины k n + n - 1, которая, очевидно, минимальна.

Существует множество методов генерации последовательности De Bruijn. Самый простой способ описания состоит в том, что последовательность де Брейна состоит из конкатенации всех слов Линдона над алфавитом, длина которого делит n в лексикографическом порядке. (Слово Линдона - это последовательность, которая лексикографически предшествует любому вращению самого себя. Отсюда следует, что слово Линдона является апериодическим.)

Существует простой алгоритм для генерации слов Линдона максимальной длины n над алфавитом в лексикографическом порядке:

  1. Начните с длины, состоящей только из лексикографически первого символа в наборе.
  2. Как можно дольше формируйте следующее слово, циклически повторяя предыдущее слово до длины n (отбрасывая лишние символы из последнего повторения, если необходимо), а затем "увеличивая" слово:
    1. Пока последний символ в слове является лексикографически большим символом в алфавите, удалите его.
    2. Если в слове все еще есть символ, измените последний символ на лексикографического преемника. Если в слове нет символов, производство будет закончено.

Чтобы сделать последовательность Де Брейна порядка n, мы создаем указанную выше последовательность слов Линдона, но мы сохраняем только те, длина которых делит n. Так как "почти все" слов Линдона максимальной длины n на самом деле имеют длину n, алгоритм можно считать O (1) на символ, или O(k n) для полной последовательности.

В конкретном случае, заданном вопросом, k == n.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню