Вопрос о строковом алгоритме - начало слова - PullRequest
4 голосов
/ 08 августа 2010

У меня есть проблема, и я не очень уверен, как ее решить, не идя по пути неэффективности.Допустим, у меня есть список слов:

  • Яблоко
  • Обезьяна
  • Дуга
  • Боюсь
  • Мост
  • Braide
  • Bray
  • Boolean

Что я хочу сделать, это обработать этот список и получить то, что каждое слово начинается с определенной глубины, например

  • a - Apple, Ape, Arc, Abraid
  • ab - Abraid
  • ar -Arc
  • ap - Apple, Ape
  • b - Мост, Braide, Bray, Boolean
  • br - Мост, Braide, Bray
  • bo - Boolean

Есть идеи?

Ответы [ 5 ]

11 голосов
/ 08 августа 2010

Вы можете использовать структуру Trie .

       (root)
         / 
        a - b - r - a - i - d
       / \   \
      p   r   e
     / \   \
    p   e   c
   /
  l
 /
e

Просто найдите нужный вам узел и получите всех его потомков, например, если я хочу ap-:

       (root)
         / 
        a - b - r - a - i - d
       / \   \
     [p]  r   e
     / \   \
    p   e   c
   /
  l
 /
e
2 голосов
/ 08 августа 2010

Возможно, вы ищете что-то вроде:

    #!/usr/bin/env python
    def match_prefix(pfx,seq):
        '''return subset of seq that starts with pfx'''
        results = list()
        for i in seq:
            if i.startswith(pfx):
                results.append(i)
        return results

    def extract_prefixes(lngth,seq):
        '''return all prefixes in seq of the length specified'''
        results = dict()
        lngth += 1
        for i in seq:
            if i[0:lngth] not in results:
                results[i[0:lngth]] = True
        return sorted(results.keys())

    def gen_prefix_indexed_list(depth,seq):
        '''return a dictionary of all words matching each prefix
           up to depth keyed on these prefixes'''
        results = dict()
        for each in range(depth):
            for prefix in extract_prefixes(each, seq):
                results[prefix] = match_prefix(prefix, seq)
        return results


    if __name__ == '__main__':
        words='''Apple Ape Arc Abraid Bridge Braide Bray Boolean'''.split()
        test = gen_prefix_indexed_list(2, words)
        for each in sorted(test.keys()):
            print "%s:\t\t" % each,
            print ' '.join(test[each])

То есть вы хотите сгенерировать все префиксы, которые присутствуют в списке слов между одним и некоторым числом, которое вы укажете (2 в этом примере). Затем вы хотите создать индекс всех слов, соответствующих каждому из этих префиксов.

Я уверен, что есть более элегантные способы сделать это. Для быстрого и легко объясненного подхода я только что построил это из простой восходящей функциональной декомпозиции кажущейся спецификации. Значения конечного результата представляют собой списки, каждый из которых соответствует заданному префиксу, затем мы начнем с функции отфильтровывать такие совпадения из наших входных данных. Если все ключи конечного результата представляют собой префиксы от 1 до N, которые появляются в нашем вводе, то у нас есть функция для их извлечения. Тогда наша спец. очень простой вложенный цикл вокруг этого.

Конечно, этот гнездовой цикл может быть проблемой. Такие вещи обычно приравнивают к эффективности O (n ^ 2). Как показано, это будет повторять исходный список C * N * N раз (C - это постоянное число, представляющее префиксы длины 1, 2 и т. Д .; тогда как N - длина списка).

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

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

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

Выполнение этого оставлено читателю в качестве упражнения.

2 голосов
/ 08 августа 2010

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

Мое предложение состоит в том, чтобы просто отсортировать список слов в лексическом порядке, а затем пройти список по порядку столько раз, сколько максимальная длина начала.

create a dictionary with keys being strings and values being lists of strings

for(i = 1 to maxBeginnigLength)
{
    for(every word in your sorted list)
    {
        if(the word's length is no less than i)
        {
            add the word to the list in the dictionary at a key
            being the beginning of the word of length i
        }

    }

}

store contents of the dictionary to the file
1 голос
/ 08 августа 2010

Используя эта реализация PHP trie даст вам около 50%. В нем есть некоторые вещи, которые вам не нужны, и нет метода «поиска по префиксу», но вы можете написать его самостоятельно достаточно просто.

$trie = new Trie();

$trie->add('Apple',   'Apple');
$trie->add('Ape',     'Ape');
$trie->add('Arc',     'Arc');
$trie->add('Abraid',  'Abraid');
$trie->add('Bridge',  'Bridge');
$trie->add('Braide',  'Braide');
$trie->add('Bray',    'Bray');
$trie->add('Boolean', 'Boolean');

Создает такую ​​структуру:

Trie Object
(
  [A] => Trie Object
  (
    [p] => Trie Object
    (
      [ple] => Trie Object
      [e] => Trie Object
    )

    [rc] => Trie Object
    [braid] => Trie Object
  )

  [B] => Trie Object
  (
    [r] => Trie Object
    (
      [idge] => Trie Object
      [a] => Trie Object
      (
        [ide] => Trie Object
        [y] => Trie Object
      )
    )

    [oolean] => Trie Object
  )
)
0 голосов
/ 08 августа 2010

Если слова были в базе данных (Access, SQL), и вы хотите получить все слова, начинающиеся с 'br', вы можете использовать:

Table Name: mytable
Field Name: mywords

"Select * from mytable where mywords like 'br*'"  - For Access - or

"Select * from mytable where mywords like 'br%'"  - For SQL
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...