Кратчайший путь к преобразованию одного слова в другое

StackOverflow https://stackoverflow.com/questions/1521958

Вопрос

Для проекта структур данных я должен найти самый короткий путь между двумя словами (например, "cat" а также "dog"), изменяя только одну букву за раз. Нам дают список слов сккрабля для поиска нашего пути. Например:

cat -> bat -> bet -> bot -> bog -> dog

Я решил проблему, используя первый поиск в ширине, но ищу что -то лучшее (я представлял словарь с три).

Пожалуйста, дайте мне несколько идей для более эффективного метода (с точки зрения скорости и памяти). Что -то нелепое и/или сложное предпочтительнее.

Я спросил одного из моих друзей (он младший), и он сказал, что есть нет эффективное решение этой проблемы. Он сказал, что я узнаю, почему, когда я прошел курс алгоритмов. Какие -нибудь комментарии по этому поводу?

Мы должны перейти от слова к слову. Мы не можем пойти cat -> dat -> dag -> dog. Анкет Мы также должны распечатать обход.

Это было полезно?

Решение

Новый ответ

Учитывая недавнее обновление, вы можете попробовать* с дистанцией хмея в качестве эвристики. Это допустимая эвристика, так как он не будет переоценить расстояние

Старый ответ

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

РЕДАКТИРОВАТЬ: Если существует постоянное количество строк, проблема решается во время полинома. В остальном, это NP-Hard (это все в Википедии). Предполагается, что ваш друг говорит о проблеме, как NP-Hard.

РЕДАКТИРОВАТЬ: Если ваши строки имеют равную длину, вы можете использовать Расстояние Хэминга.

Другие советы

С помощью словаря BFS является оптимальным, но необходимое время выполнения пропорционально его размеру (V+E). С n буквами, словарь может иметь ~ a^n Entires, где A имеет размер алфавита. Если в словаре содержится все слова, кроме того, которые должны быть в конце цепи, то вы пересекаете все возможные слова, но ничего не найдете. Это обход графика, но размер может быть экспоненциально большим.

Вы можете задаться вопросом, можно ли сделать это быстрее - просматривать структуру «разумно» и сделать это в полиномиальное время. Ответ, я думаю, нет.

Проблема:

Вам дают быстрый (линейный) способ проверить, есть ли слово в словаре, два слова u, v и должны проверить, есть ли последовательность u -> a1 -> а2 -> ... -> Ане -> v.

это np-hard.

Доказательство: возьмите немного экземпляра 3SAT, как

(P или Q или нет R) и (P или нет Q или R)

Вы начнете с 0 000 00 и должны проверить, можно ли перейти до 2 222 22.

Первым персонажем будет «закончено ли мы», три следующих бита будут контролировать P, Q, R и два следующих будут управлять пунктами.

Разрешенные слова:

  • Все, что начинается с 0 и содержит только 0 и 1
  • Все, что начинается с 2 и является законным. Это означает, что он состоит из 0 и 1 (за исключением того, что первый символ - 2, все биты положений по праву устанавливаются в соответствии с битами переменных, и они устанавливаются на 1 (так что это показывает, что формула удовлетворяется).
  • Все, что начинается как минимум с двумя 2 -м, а затем состоит из 0 и 1 (регулярное выражение: 222* (0+1)*, как 22221101, но не 2212001

Чтобы произвести 2 222 22 из 0 000 00, вы должны сделать это таким образом:

(1) Переверните соответствующие биты - например, 0 100 111 в четырех шагах. Это требует поиска решения 3SAT.

(2) Измените первый бит на 2: 2 100 111. Здесь вы будете подтверждены, это действительно решение 3SAT.

(3) Измените 2 100 111 -> 2 200 111 -> 2 220 111 -> 2 222 111 -> 2 222 211 -> 2 222 221 -> 2 222 222.

Эти правила обеспечивают соблюдение того, что вы не можете обмануть (проверить). Переход к 2 222 22 возможно только в том случае, если формула удовлетворяется, а проверка, которая является NP-Hard. Я чувствую, что это может быть еще сложнее (вероятно, #P или FNP), но NP-Hardness достаточно для этой цели, я думаю.

Редактировать: Вам может быть заинтересован в Несомненно, установить структуру данных. Анкет Это займет ваш словарь и групповые слова, которые можно достичь друг с другом. Вы также можете хранить путь от каждой вершины к корне или другой вершине. Это даст вам путь, не обязательно самый короткий.

Есть методы различной эффективности нахождение Ссылки - вы можете построить полный график для каждой длины слова, или вы можете построить BK-Tree, Например, но ваш друг прав - BFS - самый эффективный алгоритм.

Существует, однако, способ значительно улучшить вашу среду выполнения: вместо того, чтобы делать один BFS из исходного узла, выполните два сначала поиска в ширину, начиная с любого конца графика и заканчиваясь, когда вы найдете общий узел в своих пограничных наборах Анкет Количество работы, которую вы должны выполнить, примерно вдвое то, что требуется, если вы ищете только с одного конца.

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

Кроме того, все сравнения STRNCMP (при условии, что вы сделали все строчное), могут быть сравнениями MEMCMP или даже развернутыми сравнениями, которые могут быть ускорением.

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

Это типичный динамическое программирование проблема. Проверьте проблему редактирования расстояния.

То, что вы ищете, называется расстоянием редактирования. Есть много разных типов.

Из (http://en.wikipedia.org/wiki/edit_distance): «В теории информации и информатики расстояние редактирования между двумя строками символов - это количество операций, необходимых для преобразования одного из них в другой».

В этой статье о Jazzy (API проверки орфографии Java) есть хороший обзор подобных сравнений (это аналогичная проблема, обеспечивающая предлагаемые исправления) http://www.ibm.com/developerworks/java/library/j-jazzy/

Вы могли бы найти самую длинную общую последующую последовательность и, следовательно, найти буквы, которые должны быть изменены.

Мое ощущение, что ваш друг прав, в том смысле, что нет более эффективного решения, но это приводит к тому, что вы каждый раз перезагружаете словарь. Если бы вы сохранили запущенную базу данных общих переходов, то, конечно, был бы более эффективный метод поиска решения, но вам нужно заранее генерировать переходы и выяснить, какие переходы будут полезны (поскольку вы не можете генерировать Они все!), вероятно, собственное искусство.

bool isadjacent(string& a, string& b)
{
  int count = 0;  // to store count of differences
  int n = a.length();

  // Iterate through all characters and return false
  // if there are more than one mismatching characters
  for (int i = 0; i < n; i++)
  {
    if (a[i] != b[i]) count++;
    if (count > 1) return false;
  }
  return count == 1 ? true : false;
}

// элемент очереди для хранения слов и минимальной длины цепи // для достижения слова.

struct QItem
{
  string word;
  int len;
};

// Возвращает длину самой короткой цепи, чтобы достичь «цели» из «старта» //, используя минимальное количество смежных движений. D - словарь

int shortestChainLen(string& start, string& target, set<string> &D)
{
  // Create a queue for BFS and insert 'start' as source vertex
  queue<QItem> Q;
  QItem item = {start, 1};  // Chain length for start word is 1
  Q.push(item);

  // While queue is not empty
  while (!Q.empty())
  {
    // Take the front word
    QItem curr = Q.front();
    Q.pop();

    // Go through all words of dictionary
    for (set<string>::iterator it = D.begin(); it != D.end(); it++)
    {
        // Process a dictionary word if it is adjacent to current
        // word (or vertex) of BFS
        string temp = *it;
        if (isadjacent(curr.word, temp))
        {
            // Add the dictionary word to Q
            item.word = temp;
            item.len = curr.len + 1;
            Q.push(item);

            // Remove from dictionary so that this word is not
            // processed again.  This is like marking visited
            D.erase(temp);

            // If we reached target
            if (temp == target)
              return item.len;
        }
    }
  }
  return 0;
}

// Driver program
int main()
{
  // make dictionary
  set<string> D;
  D.insert("poon");
  D.insert("plee");
  D.insert("same");
  D.insert("poie");
  D.insert("plie");
  D.insert("poin");
  D.insert("plea");
  string start = "toon";
  string target = "plea";
  cout << "Length of shortest chain is: "
       << shortestChainLen(start, target, D); 
  return 0; 
}

Скопировано из: https://www.geeksforgeeks.org/word-ladder-lue-of-shortest-chain-to-reach-a-target-word/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top