Проблема с производительностью при получении всех слов с общим префиксом в дереве префиксов

0

У меня есть дерево префикса для хранения огромной коллекции слов. Прямо сейчас, если я хочу найти все слова с общим префиксом, скажем, "a", я сначала извлекаю первый узел, содержащий a, а затем исчерпывающий поиск в методе Depth First в дочерних узлах первого узла. Хотя эта идея выглядит наивной и простой, она на самом деле патетически медленна, если возможное количество слов с общим префиксом ОЧЕНЬ ВЫСОКО (> 20 КБ). Есть ли другой способ эффективно получить все слова, начинающиеся с общего префикса? Или я должен принять другую структуру данных? Спасибо вам за продвижение.

EDIT1 В основном я создаю полное слово, посещая каждый узел и добавляя символ постепенно. Все слова затем сохраняются в векторном контейнере. И да, у меня рекурсивная реализация.

EDIT2

vector<int> getNonEmptyEdgeIndices(Node* parent) {
    vector<int> indices;
    for(int i=0; i<EDGE; i++) {
        if (parent->edges[i] != NULL) {
            indices.push_back(i);
        }
    }
    return indices; 
}

vector<string> getSubsequentStrings(vector<string> wordsStartingWith, Node* node, string prefix) {
    vector<int> indices = getNonEmptyEdgeIndices(node);

    // push the word to the container if node is a leaf 
    if (indices.empty()) {
        wordsStartingWith.push_back(prefix);
        return wordsStartingWith;
    }

    // if frequency is set in node, push the word but still continue recursion
    if (node->frequency != 0) {
        wordsStartingWith.push_back(prefix);
    }

    // look all the children of the node
    for(unsigned int i=0; i<indices.size(); i++) {
        string newPrefix = prefix + getNodeChar(indices[i]);
        Node* child = node->edges[indices[i]];

        // recursively get the prefix for all children
        wordsStartingWith = getSubsequentStrings(wordsStartingWith, child, newPrefix);  
    }

    return wordsStartingWith;
}

vector<string> Trie::getWordsStartingWith(string prefix) {
    vector<string> wordsStartingWith;
    Node* lastNode = getLastNode(prefix);

    if (lastNode != NULL) {
        wordsStartingWith = getSubsequentStrings(wordsStartingWith, lastNode, prefix);
    }
    return wordsStartingWith;
}

ИЗМЕНИТЬ 3 РЕШЕНЫ !!! На самом деле была проблема с моей реализацией. Я передавал этот огромный контейнер векторной строки в рекурсивных вызовах, что было на самом деле проблемой. Спасибо всем за ваш добрый совет.

  • 1
    Некоторые детали реализации были бы хорошими. Префиксные деревья должны быть хороши в этом, они могут делать это в линейном пространстве и времени. Возможно, ваша проблема в том, что вы копируете все строки из дерева, чтобы сгенерировать массив, прежде чем что-то с ним делать? Возможно, вместо этого вы хотите написать итератор, который позволит вам делать то, что вы хотите с каждым элементом, не копируя их все?
  • 1
    Можете ли вы поделиться кодом, который вы пробовали? Это может быть код, а не структура данных, которая может быть улучшена.
Показать ещё 5 комментариев
Теги:
performance
algorithm
prefix-tree

1 ответ

0

На самом деле TRIE + DFT уже является достаточно хорошим решением для вашего дела. Его временной сложностью является O(M+B^M) где M - максимальная длина слова, а B - постоянное число возможных букв (обычно B=26). Хотя это экспоненциально, но на практике это может быть намного быстрее, чем вы думаете, потому что дерево TRIE очень редкое, а M - небольшое число.

Более простым (не гарантированным лучшим) решением является сортировка всех слов в массиве. Затем вы можете получить то, что хотите, путем двоичного поиска массива для первого и последнего слова, у которого есть целевой префикс, точно так же, как вы используете английский словарь. Сортировка принимает O(NlogN) а поиск принимает O(MlogN) где N - количество слов. Это многочлен.

Если вам действительно нужна скорость, вы можете почти всегда оплачивать пространство памяти для обмена. В этом случае вы можете связать каждое слово со всеми его префиксными узлами указателями во время построения дерева TRIE. Тогда временная сложность будет сведена к O(M+N) которая будет чрезвычайно быстрой. Но, с другой стороны, это займет пространственную сложность O(NM). Предположим, что у вас есть миллион слов с 5 буквами в среднем, вы потратили бы около 150 КБ памяти на указатели.

Ещё вопросы

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