Разбить строку на строку допустимых слов с помощью динамического программирования

Мне нужно найти алгоритм динамического программирования для решения этой проблемы. Я пытался, но не мог понять. Вот проблема:

Вам дана строка из n символов s[1...n], которую вы считаете поврежденным текстовым документом, в котором исчезли все знаки препинания (так что это выглядит примерно как "это было лучшее время..."). Вы хотите восстановить документ, используя словарь, который доступен в виде булевой функции dict(*), такой что для любой строки w dict(w) имеет значение 1, если w является допустимым словом, и имеет значение 0 в противном случае.

  1. Предложите алгоритм динамического программирования, определяющий, можно ли преобразовать строку s[*] в последовательность допустимых слов. Время выполнения должно быть не более O(n^2), если предположить, что каждый вызов dict занимает единицу времени.
  2. В случае, если строка действительна, заставьте ваш алгоритм выводить соответствующую последовательность слов.

person Pet    schedule 15.03.2011    source источник
comment
Ну это упражнение из учебника. У меня нет решений упражнений, и я не знаю, как решить эту проблему.   -  person Pet    schedule 15.03.2011
comment
Первое, что приходит на ум - двусмысленность. Предположим, в вашем словаре есть слова «была», «она» и «шайба». Однако вы можете просто предпочесть самые короткие слова. Подождите, нет... вы можете отрезать часть слова и сделать строку недействительной - например, поймать «авто» из «автоматического».   -  person alxx    schedule 15.03.2011
comment
Вы пытались сначала найти ответ? На SO уже есть несколько вопросов об этой проблеме - stackoverflow.com/questions /4755157/split-string-into-words , stackoverflow.com/questions/3553958/ , stackoverflow.com/questions/3466972/ . В некоторых из них упоминаются решения для динамического программирования.   -  person hoha    schedule 15.03.2011


Ответы (6)


Пусть длина вашего сжатого документа будет N.

Пусть b(n) — логическое значение: true, если документ можно разбить на слова, начиная с позиции n в документе.

b(N) истинно (поскольку пустую строку можно разбить на 0 слов). Имея b(N), b(N - 1), ... b(N - k), вы можете построить b(N - k - 1), рассматривая все слова, начинающиеся с символа N - k - 1. Если есть любое такое слово, w, с b(N - k - 1 + len(w)) установлено, затем установите b(N - k - 1) в true. Если такого слова нет, установите b(N - k - 1) в false.

В конце концов, вы вычисляете b(0), который говорит вам, можно ли весь документ разбить на слова.

В псевдокоде:

def try_to_split(doc):
  N = len(doc)
  b = [False] * (N + 1)
  b[N] = True
  for i in range(N - 1, -1, -1):
    for word starting at position i:
      if b[i + len(word)]:
        b[i] = True
        break
  return b

Есть несколько трюков, которые вы можете сделать, чтобы сделать «слово, начинающееся с позиции i», эффективным, но вас попросят использовать алгоритм O (N ^ 2), поэтому вы можете просто искать каждую строку, начинающуюся с i в словаре.

Чтобы сгенерировать слова, вы можете либо изменить приведенный выше алгоритм для хранения хороших слов, либо просто сгенерировать его следующим образом:

def generate_words(doc, b, idx=0):
  length = 1
  while true:
    assert b(idx)
    if idx == len(doc): return
    word = doc[idx: idx + length]
    if word in dictionary and b(idx + length):
       output(word)
       idx += length
       length = 1

Здесь b — логический массив, сгенерированный из первой части алгоритма.

person Community    schedule 15.03.2011
comment
Разве это не неэффективно, если вы рассматриваете все слова, которые начинаются с символа N - k - 1. Лучшим методом будет b[i] = true if there exists i <= j < N such that dict(s[i..j]) and b[j+1..N-1] - person Minh Pham; 07.03.2013

Формализовать то, что предложил @MinhPham.

Это решение для динамического программирования.

Дана строка str, пусть

b[i] = true, если подстрока str[0...i] (включительно) может быть разбита на допустимые слова.

Добавьте к строке какой-нибудь начальный символ, например !, чтобы представить пустое слово. ул = "!" + ул

Базовым случаем является пустая строка, поэтому

б[0] = истина.

Для итеративного случая:

b[j] = true, если b[i] == true и str[i..j] является словом для всех i ‹ j

person mingxiao    schedule 03.07.2013

O(N^2) Dp ясен, но если вы знаете слова из словаря, я думаю, вы можете использовать некоторые предварительные вычисления, чтобы получить его еще быстрее в O(N). Aho-Corasick

person mariusgherman    schedule 15.03.2011

Решение dp в С++:

int main()
{
    set<string> dict;
    dict.insert("12");
    dict.insert("123");
    dict.insert("234");
    dict.insert("12345");
    dict.insert("456");
    dict.insert("1234");
    dict.insert("567");
    dict.insert("123342");
    dict.insert("42");
    dict.insert("245436564");
    dict.insert("12334");

    string str = "123456712334245436564";

    int size = str.size();
    vector<int> dp(size+1, -1);
    dp[0] = 0;
    vector<string > res(size+1);
    for(int i = 0; i < size; ++i)
    {
        if(dp[i] != -1)
        {
            for(int j = i+1; j <= size; ++j)
            {
                const int len = j-i;
                string substr = str.substr(i, len);
                if(dict.find(substr) != dict.end())
                {
                    string space = i?" ":"";
                    res[i+len] = res[i] + space + substr;
                    dp[i+len] = dp[i]+1;
                }
            }
        }
    }
    cout << *dp.rbegin() << endl;
    cout << *res.rbegin() << endl;

    return 0;
}
person hidayat    schedule 15.03.2011
comment
Почему бы вам не дать описание того, что вы сделали, и не объяснить, почему вы это сделали? - person Minh Pham; 07.03.2013
comment
@tobias, не могли бы вы объяснить его алгоритм - person EmptyData; 21.05.2014
comment
Просто случайный код никому не поможет. Вы должны предоставить объяснение. - person adijo; 13.06.2014

Строка s[] потенциально может быть разделена более чем одним способом. Приведенный ниже метод находит максимальное количество слов, на которые мы можем разделить s[]. Ниже приведен скетч/псевдокод алгоритма.

bestScore[i] -> Сохраняет максимальное количество слов, в которых можно разделить первые i символов (в противном случае было бы MINUS_INFINITY)

for (i = 1 to n){
     bestScore[i] = MINUS_INFINITY
     for (k = 1 to i-1){
        bestScore[i] = Max(bestSCore[i], bestScore[i-k]+ f(i,k))
     }
 }

Где f(i,k) определяется как:

f(i,k) = 1 : if s[i-k+1 to i] is in dictionary
       = MINUS_INFINITY : otherwise

bestScore[n] будет хранить максимальное количество слов, на которые можно разделить s[] (если значение равно MINUS_INFINIY, s[] нельзя разделить)

Ясно, что время работы O (n ^ 2)

Поскольку это выглядит как упражнение из учебника, я не буду писать код для восстановления фактических позиций разделения.

person Mahak Patidar    schedule 15.03.2011

Ниже приведено решение O (n ^ 2) для этой проблемы.

void findstringvalid() {
string s = "itwasthebestoftimes";
set<string> dict;
dict.insert("it");
dict.insert("was");
dict.insert("the");
dict.insert("best");
dict.insert("of");
dict.insert("times");

vector<bool> b(s.size() + 1, false);
vector<int> spacepos(s.size(), -1);
//Initialization phase
b[0] = true; //String of size 0 is always a valid string
for (int i = 1; i <= s.size(); i++) {
    for (int j = 0; j <i; j++) {
       //string of size s[ j... i]
       if (!b[i]) {
           if (b[j]) {
              //check if string "j to i" is in dictionary
              string temp = s.substr(j, i - j);
              set<string>::iterator it = dict.find(temp);
              if (it != dict.end()) {
                  b[i] = true;
                  spacepos[i-1] = j;
              }
           }
        }
    }
}
if(b[s.size()])
    for (int i = 1; i < spacepos.size(); i++) {
        if (spacepos[i] != -1) {
            string temp = s.substr(spacepos[i], i - spacepos[i] + 1);
            cout << temp << " ";
    }
    }
}
person Pankaj Mistry    schedule 27.09.2016
comment
Ваш словарь не включает все возможные слова в строке. Например, a, as и he — допустимые слова, которые можно найти в этой подстроке. - person Phil Glau; 17.05.2019