← Все вопросы

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

Задан 17 месяцев назад623 просмотров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.

Ваш ответ

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