C ++ / C / Java: анаграммы - от исходной строки до цели; - PullRequest
2 голосов
/ 04 марта 2010

Я пытаюсь решить эту проблему: http://uva.onlinejudge.org/external/7/732.html. Для данного примера они дают нам исходное слово, например TRIT и целевую "анаграммированную" строку, TIRT .

Цель: Мы должны вывести все действительные последовательности 'i' и 'o' (соответственно push и pop's), которые производят целевую строку из исходной строки.

Итак, я думал о том, чтобы вычислить все перестановки «i» и «o», но обрезал следующие случаи:

1) если текущая перестановка начинается с 'o', прекратите проверку, поскольку все следующие перестановки начнутся с этой команды pop, а извлечение чего-либо из пустого стека является недопустимой командой.

2) , если в середине проверки найдена команда 'o' и в стеке ничего нет, пропустите этот случай.

3) если команда 'i' найдена и во входной строке ничего нет, пропустите этот случай.

4) , если найдена команда 'o', и ожидаемый в данный момент символ не является только что выданным символом, пропустите этот случай, поскольку он никогда не достигнет целевой строки.

5) не выполнять поиск, если входная и целевая строки имеют разную длину.

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

Я знаю теорию: возможно, перестановки и возвращаемся назад. У меня просто слишком много трудностей с его реализацией.

Может кто-нибудь поделиться со мной кодом и / или идеями, пожалуйста?

P.S .: Любое предложение, которое может сократить время выполнения, будет приветствоваться, конечно.

Ответы [ 2 ]

9 голосов
/ 04 марта 2010

Это интересная проблема, и я полагаю, что приведенный ниже подход (find all trees which make the *from* string their preorder and the *to* string their inorder, подробности ниже), если он будет реализован должным образом, будет очень быстрым: гораздо лучше, чем рекурсия методом грубой силы, поскольку он просто «знает», что возможно подзадачи находятся на каждом этапе.

На самом деле я провел небольшой эксперимент и сравнил его с одним из приведенных ответов о грубой силе. (Примечание: код C # находится в нижней части потока). Алгоритм предварительного заказа / заказа не является оптимальным и может быть улучшен. Разумеется, алгоритм грубой силы имеет преимущество в том, что он лаконичен и вполне может быть достаточно хорошим (и более эффективным) для задач меньшего размера.

Ниже приведены результаты. Особенно обратите внимание на последние два результата для более длинных струн, для которых подход «предзаказ / порядок» намного быстрее, чем «грубая сила». Тот факт, что существует большое различие между количеством решаемых подзадач, должен убедить вас, что это происходит не только из-за данных, но поскольку строки становятся длиннее (возможно, с более повторяющимися символами), решение грубой силы по сути должно иметь дело с намного больше ненужных подзадач. Возможно, можно сделать какие-либо заметки с помощью метода грубой силы, но это кажется очень трудным.

------------------------
bahama(Length:6)
bahama(Length:6)
PreorderInorder
        TimeTaken: 00:00:00.0230000
        Number of recursive calls: 20
        Number of results returned: 4
Brute Force
        TimeTaken: 00:00:00.0030000
        Number of recursive calls: 47
        Number of results returned: 4
------------------------
madameric(Length:9)
adammcire(Length:9)
PreorderInorder
        TimeTaken: 00:00:00
        Number of recursive calls: 28
        Number of results returned: 4
Brute Force
        TimeTaken: 00:00:00
        Number of recursive calls: 107
        Number of results returned: 4
------------------------
trittrottotr(Length:12)
tirttorttort(Length:12)
PreorderInorder
        TimeTaken: 00:00:00.0010000
        Number of recursive calls: 103
        Number of results returned: 63
Brute Force
        TimeTaken: 00:00:00.0010000
        Number of recursive calls: 1301
        Number of results returned: 63
------------------------
bahamabhahambahamamadambahamabhahambahama(Length:41)
bahamabhahambahamamadambahamabhahammahaba(Length:41)
PreorderInorder
        TimeTaken: 00:00:01.1710000
        Number of recursive calls: 2059
        Number of results returned: 97472
Brute Force
        TimeTaken: 00:00:18.2610000
        Number of recursive calls: 41784875
        Number of results returned: 97472
------------------------
bahamabhahambahamamadambahamabhahambahama(Length:41)
bahamabhahambahamamadambahamabhahambahama(Length:41)
PreorderInorder
        TimeTaken: 00:00:00.1790000
        Number of recursive calls: 315
        Number of results returned: 20736
Brute Force
        TimeTaken: 00:00:17.1680000
        Number of recursive calls: 41062923
        Number of results returned: 20736


Для более коротких строк грубая сила побеждает, так как есть меньшие накладные расходы, но скорость другого алгоритма действительно показывает, когда строка становится длиннее. В любом случае, для более коротких струн вы можете использовать грубую силу и перейти к подходу «предзаказ / порядок» для более длинных, чтобы получить лучшее из обоих миров.


Теперь приведено описание предлагаемого метода:

Рассмотрим следующее дерево:

        m
      /    \
     a      m
    / \    / \
   .   d  .   .
      / \  
     .   a 
        / \
       .   .

где. являются нулевыми узлами.

Рассмотрим предварительный порядок, в котором вы также выводите нулевой узел (точка = '.').

Это дает нам предварительный заказ: m a . d . a . . m . .

Рассмотрим порядок без нулевых узлов: a d a m m

Теперь рассмотрим следующее:

Вы берете предзаказ: m a . d . a .. m .. и каждый раз, когда видите ненулевое (или не точечное), вы нажимаете на стек. Каждый раз, когда вы видите ноль (или точку), вы выдвигаете вершину стека. Вы можете игнорировать последний., Так как это приводит к появлению пустого стека.

Мы можем запустить его вручную:

m a . d . a . . m ..    | Stack =                    | Output = 

Push m 

  a . d . a . . m ..    | Stack = m                  | Output = 

Push a 

  . d . a . . m ..      | Stack = a m                | Output = 

Pop 

    d . a . . m ..      | Stack =  m                 | Output = a

Push d

      . a . . m ..      | Stack =  d m               | Output = a

Pop 
        a . . m ..      | Stack =    m               | Output = a d

Push a
          . . m ..      | Stack =   a m              | Output = a d

Pop, Pop (sorry getting a little lazy)

              m ..      | Stack =                    | Output = a d a m 

Push m, Pop
                 .      | Stack =                    | Output = a d a m m.


Теперь сравните вывод этого с порядком. Это тот же !

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

Краткий набросок доказательства: обратите внимание, что количество нулевых узлов = 1 + количество ненулевых узлов. Это означает, что когда вы закончили выталкивать левое дерево, вы извлекаете корень из-за последнего нулевого значения левого дерева, и, таким образом, все эти нажатия и выталкивания преобразуются в (левый) корень (справа).

Таким образом, учитывая строку A (например, мадам), которую нужно преобразовать в строку B (например, adamm), используя только нажатия и нажатия, мы имеем следующий подход к проблеме:

Find all trees which have A as the preorder and B as the inorder

Предзаказ этого дерева, выводящий push для ненулевых и pop для нулевых узлов (игнорируя последний push) должен дать требуемую последовательность.

Поиск дерева с заданным предзаказом / порядком является стандартной проблемой и имеет много быстрых решений. Для этой проблемы вам, вероятно, просто нужно немного подправить одно из этих решений.

Кроме того, некоторые из преимуществ этого подхода

  • Легко зная, какую подзадачу решить.
  • Вышеуказанный пункт помогает реализовать запоминание (см. Код ниже).
  • Этот алгоритм также можно легко распараллелить.

Я предполагаю, что вы действительно хотите написать свой собственный код на C / C ++ / Java, поэтому я предоставлю код прототипа C #, который у меня есть.

StackAnagram.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SO
{
    public class StackAnagram
    {
        public static int count = 0;
        static Dictionary<string, Dictionary<string, List<string>>> memoized = 
                     new Dictionary<string, Dictionary<string, List<string>>>();

        public static List<string> Instructions(string from, string to)
        {
            count = 0;
            if (from.Length != to.Length)
            {
                return new List<string>();
            }

            List<string> l = Instructions(from, 0, from.Length, to, 0, to.Length);
            return l;
        }

        private static bool IsAnagram(string from, string to, 
                              int startF, int endF, int startTo, int endTo)
        {
            Dictionary<char, int> d = new Dictionary<char, int>();

            for (int i = startF; i < endF; i++)
            {
                if (d.ContainsKey(from[i]))
                {
                    d[from[i]]++;
                }
                else
                {
                    d[from[i]] = 1;
                }
            }
            for (int i = startTo; i < endTo; i++)
            {
                if (d.ContainsKey(to[i]))
                {
                    d[to[i]]--;
                    if (d[to[i]] == 0)
                    {
                        d.Remove(to[i]);
                    }
                }
                else
                {
                    return false;
                }
            }

            if (d.Count > 0)
            {
                return false;
            }

            return true;
        }

        private static List<string> Instructions(string from, 
                  int startF, int endF, string to, int startTo, int endTo)
        {

            List<string> inst;

            // Null tree.
            if (startF >= endF || startTo >= endTo)
            {
                inst = new List<string>();
                inst.Add("o ");
                count++;
                return inst;
            }

            string subFrom = from.Substring(startF, endF - startF);
            string subTo = to.Substring(startTo, endTo - startTo);
            Dictionary<string, List<string>> dict;
            if (memoized.TryGetValue(subFrom, out dict))
            {
                if (dict.TryGetValue(subTo, out inst))
                {
                    return inst;
                }
            }
            else
            {
                memoized[subFrom] = new Dictionary<string, List<string>>();
            }

            count++;
            inst = new List<string>();

            if (!IsAnagram(from, to, startF, endF, startTo, endTo))
            {
                goto ret;
            }

            for (int j = 0; j < endTo - startTo; j++)
            {
                // Found possible root
                if (from[startF] == to[startTo + j])
                {
                    List<string> leftInst = Instructions(from, startF + 1, 
                                       startF + j + 1, to, startTo, startTo + j);

                    List<string> rightInst = Instructions(from, startF + j + 1, 
                                       endF, to, startTo + j + 1, endTo);

                    if (rightInst.Count <= 0)
                    {
                        continue;
                    }

                    foreach (string l in leftInst)
                    {
                        foreach (string r in rightInst)
                        {
                            inst.Add("i " + l + r);
                        }
                    }
                }
            }
        ret:
            memoized[subFrom][subTo] = inst;
            return inst;
        }
    }

    public class StackAnagramBrute
    {
        public static int count = 0;

        static void anagram(String s1, String s2, String stack, 
                                String instr, List<string> insts)
        {
            count++;
            if (s2.Length == 0)
            {
                if (s1.Length == 0 && stack.Length == 0)
                {
                    insts.Add(instr.Trim());
                }
                return;
            }
            if (!(s1.Length == 0))
            {
                anagram(s1.Substring(1), s2, s1[0] + stack, instr + "i ", insts);
            }
            if (!(stack.Length == 0) && stack[0] == s2[0])
            {
                anagram(s1, s2.Substring(1), stack.Substring(1), instr + "o ", 
                                                                         insts);
            }
        }

        public static List<string> anagram(String s1, String s2)
        {
            count = 0;
            if (s1.Length != s2.Length)
            {
                return new List<string>();
            }

            List<string> l = new List<string>();
            anagram(s1, s2, "", "", l);
            return l;
        }
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace SO
{
    class Program
    {
        static void Main(string[] args)
        {
                string[] from = { "bahama", "madameric", "trittrottotr", 
"bahamabhahambahamamadambahamabhahambahama", "bahamabhahambahamamadambahamabhahambahama" };
                string[] to = { "bahama", "adammcire", "tirttorttort", 
"bahamabhahambahamamadambahamabhahammahaba", "bahamabhahambahamamadambahamabhahambahama" };

                for (int i = 0; i < from.Length; i++)
                {
                    CompareAlgorithms(from[i], to[i]);
                }

        }

        static void CompareAlgorithms(string from, string to)
        {
            Console.WriteLine("------------------------");
            Console.WriteLine(from + "(Length:" + from.Length + ")");
            Console.WriteLine(to + "(Length:" + to.Length + ")");

            DateTime start = DateTime.Now;
            List<string> inst = StackAnagram.Instructions(from, to);
            DateTime end = DateTime.Now;
            TimeSpan t = end - start;
            Display("PreorderInorder", t, StackAnagram.count, inst.Count);

            DateTime startBrute = DateTime.Now;
            List<string> instBrute = StackAnagramBrute.anagram(from, to);
            DateTime endBrute = DateTime.Now;

            TimeSpan tBrute = endBrute - startBrute;

            Display("Brute Force", tBrute, StackAnagramBrute.count, instBrute.Count);
        }

        static void Display(string method, TimeSpan t, int callCount, int resultCount)
        {

            Console.WriteLine(method);

            Console.Write("\t");
            Console.Write("TimeTaken: ");
            Console.WriteLine(t.ToString());

            Console.Write("\t");
            Console.Write("Number of recursive calls: ");
            Console.WriteLine(callCount);

            Console.Write("\t");
            Console.Write("Number of results returned: ");
            Console.WriteLine(resultCount);
        }
    }
}
5 голосов
/ 04 марта 2010

Это первое итерационное решение поучительно. Это не самый эффективный способ, поскольку он использует String повсеместно, но это хорошее место для начала.

import java.util.*;

public class StackAnagram {

    static void anagram(String s1, String s2, String stack, String instr) {
        if (s2.isEmpty()) {
            if (s1.isEmpty() && stack.isEmpty()) {
                System.out.println(instr.trim());
            }
            return;
        }
        if (!s1.isEmpty()) {
            anagram(s1.substring(1), s2, s1.charAt(0) + stack, instr + "i ");
        }
        if (!stack.isEmpty() && stack.charAt(0) == s2.charAt(0)) {
            anagram(s1, s2.substring(1), stack.substring(1), instr + "o ");
        }
    }

    static void anagram(String s1, String s2) {
        System.out.println("[");
        anagram(s1, s2, "", "");
        System.out.println("]");
    }

    public static void main(String args[]) {
        anagram("madam", "adamm");
        anagram("bahama", "bahama");
        anagram("long", "short");
        anagram("eric", "rice");
        anagram("ericc", "rice");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...