Поиск строки при вводе символа

У меня есть контакты, хранящиеся на моем мобильном телефоне. Допустим, мои контакты

Ram

Hello

Hi

Feat

Eat

At

Когда я набираю букву 'A', я должен получить все соответствующие контакты: "Ram, Feat, Eat, At".

Теперь я набираю еще одну букву T. Теперь моя общая строка "AT" теперь моя программа должна повторно использовать результаты предыдущего поиска "A". Теперь он должен вернуть мне "Feat, Eat, At"

Разработайте и разработайте для этого программу.

Это вопрос интервью в Samsung mobile development

Я попытался решить с помощью Trie data structures. Не удалось получить хорошее решение для повторного использования уже найденных результатов строки. Я также пробовал решение со структурой данных словаря, решение имеет тот же недостаток, что и Trie.

Вопрос: как мне искать контакты для каждой буквы, набрав повторное использование результатов поиска ранее найденной строки? Какую структуру данных и алгоритм следует использовать для эффективного решения проблемы.

Я не прошу программы. Поэтому язык программирования для меня несуществен.

Состояние машины представляется хорошим решением. Кто-нибудь имеет предложение?

Решение должно быть достаточно быстрым для миллионов контактов.

Ответ 1

Это зависит от количества предметов, которые вы ищете. Если это относительно небольшой список, вы можете сделать проверку string.contains на все. Поэтому, когда пользователь вводит "A", вы просматриваете весь список:

for each contact in contacts
    if contact.Name.Contains("A")
        Add contact to results

Затем пользователь вводит "T", и вы последовательно просматриваете предыдущие возвращенные результаты:

for each contact in results
    if contact.Name.Contains("AT")
        Add contact to new search results

Все становится интереснее, если список контактов огромен, но для количества контактов, которые вы обычно имели в телефоне (тысячи было бы много!), это будет работать очень хорошо.

Если интервьюер сказал: "Используйте результаты предыдущего поиска для нового поиска", то я подозреваю, что это тот ответ, который он искал. Для создания нового дерева суффиксов потребуется больше времени, чем просто выполнить поиск по предыдущему набору результатов.

Это можно немного оптимизировать, сохранив позицию подстроки вместе с контактом, чтобы все, что вам нужно сделать в следующий раз, - проверить, соответствует ли следующий символ ожидаемому, но это усложняет алгоритм немного (вы должны рассматривать первый поиск как особый случай, и вам нужно явно проверять длины строк и т.д.), и вряд ли это принесет большую пользу после первых нескольких символов, потому что размер просматриваемого списка будет быть довольно маленьким. Чистый последовательный поиск с проверкой contains будет довольно быстрым. Пользователи не заметили бы несколько микросекунд, которые вы сохранили бы с этой оптимизацией.

Обновление после редактирования вопроса

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

В любом случае, вы можете ускорить первоначальный поиск, создав индекс bigram. То есть для каждого биграмма (последовательность из двух символов) создайте список имен, которые содержат этот bigram. Вы также захотите создать список строк для каждого отдельного символа. Итак, учитывая ваш список имен, у вас будет:

r - ram
a - ram, feat, eat, a
m - ram
h - hello, hi
...
ra - ram
am - ram
...
at - feat, eat, at
...
etc.

Я думаю, вы поняли.

Этот индекс bigram сохраняется в словаре или хэш-карте. Есть только 325 возможных биграмм на английском языке и, конечно, 26 букв, поэтому ваш словарь будет иметь максимум 351 записей.

Итак, вы почти мгновенно просматриваете 1- и 2-символьные имена. Как это вам поможет?

Анализ текста проекта Gutenberg показывает, что наиболее распространенный биграмм на английском языке встречается только в 3,8% случаев. Я понимаю, что имена не будут делиться именно этим распределением, но это довольно хороший примерный номер. Итак, после ввода первых двух символов вы, вероятно, будете работать с менее чем 5% от общего количества имен в вашем списке. Пять процентов миллиона - 50 000. Имея всего 50 000 имен, вы можете начать использовать алгоритм последовательного поиска, который я описал первоначально.

Стоимость этой новой структуры не так уж плоха, хотя она достаточно дорогая, поэтому я бы, конечно же, попробовал простой последовательный поиск. Это будет стоить вам дополнительных 2 миллионов ссылок на имена, в худшем случае. Вы могли бы уменьшить это до миллиона дополнительных ссылок, если вы создадите двухуровневый trie, а не словарь. Это займет немного больше времени для поиска и отображения результатов поиска с одним символом, но недостаточно для того, чтобы быть заметным для пользователя.

Эта структура также очень легко обновляется. Чтобы добавить имя, просто просмотрите строку и введите записи для соответствующих символов и биграмм. Чтобы удалить имя, перейдите к имени, извлечению битрамов, и удалите имя из соответствующих списков в индексе bigram.

Ответ 2

Посмотрите "обобщенное дерево суффиксов", например. https://en.wikipedia.org/wiki/Generalized_suffix_tree. Для фиксированного размера алфавита эта структура данных дает асимптотически оптимальное решение для нахождения всех совпадений z подстроки длины m в наборе строк в O (z + m) времени. Таким образом, вы получаете такую ​​же выгоду, как если бы вы ограничили свой поиск совпадениями для предыдущего префикса. Также структура имеет оптимальное пространство O (n) и время сборки, где n - общая длина всех ваших контактов. Я считаю, что вы можете изменить структуру так, что вы просто найдете строки k, которые содержат подстроку в O (k + m) времени, но, как правило, вы, вероятно, не должны иметь слишком много совпадений для каждого контакта, которые имеют совпадение, поэтому это может даже не нужно.

Ответ 3

То, что я собираюсь сделать, - отслеживать строку so far matched. Предположим, что на первом шаге мы идентифицируем строки, в которых есть "A", и мы сохраняем следы позиций "A". Затем на следующем шаге мы перебираем только эти строки и вместо того, чтобы искать их полностью, мы проверяем только появление "Т" в качестве следующего символа "А", мы сохранили след на предыдущем шаге и т.д.