← Все вопросы

Бор для автодополнения: как по узлу быстро отдать все слова с данным префиксом?

Задан 20 месяцев назад640 просмотров2 ответа
8

Делаю автодополнение: по введённому префиксу нужно вернуть все слова словаря, которые с него начинаются (или хотя бы их количество). Бор для поиска префикса понятен, но как из узла-конца-префикса собрать все слова поддерева эффективно?

2 ответа

13
✓ Принятый ответ — помог автору

Два сценария.

Если нужно только количество слов с префиксом — заведите в каждом узле счётчик cnt, увеличивая его при вставке вдоль всего пути. Тогда ответ — cnt узла, до которого довёл префикс, за O(|prefix|).

void insert(const string& s){
    int v=0;
    for(char ch:s){ int c=ch-'a';
        if(t[v].next[c]==-1){t[v].next[c]=t.size(); t.emplace_back();}
        v=t[v].next[c]; t[v].cnt++;          // считаем проходящие через узел слова
    }
    t[v].leaf=true;
}
int count_prefix(const string& p){
    int v=0;
    for(char ch:p){ int c=ch-'a'; if(t[v].next[c]==-1) return 0; v=t[v].next[c]; }
    return t[v].cnt;
}

Если нужны сами слова — дойдите до узла префикса (O(|prefix|)), затем сделайте DFS по поддереву, накапливая символы и выписывая слово в каждом leaf. Это O(|prefix| + суммарная длина выданных слов) — оптимально, ведь вы обязаны хотя бы напечатать ответ.

void dfs(int v, string& cur, vector<string>& out){
    if(t[v].leaf) out.push_back(cur);
    for(int c=0;c<26;++c) if(t[v].next[c]!=-1){
        cur.push_back('a'+c); dfs(t[v].next[c], cur, out); cur.pop_back();
    }
}

DFS по детям 0..25 даёт слова сразу в лексикографическом порядке — удобно для подсказок.

4

Если автодополнений миллионы и нужно отдавать топ-k популярных — храните в листе частоту запроса слова и в каждом узле максимум по поддереву; тогда обход можно делать жадно (priority_queue по «максимум поддерева»), не разворачивая весь бор. Это уже похоже на то, как реальные движки подсказок ограничивают выдачу. Но на учебных задачах хватает простого DFS.

Ваш ответ

, чтобы ответить на вопрос.