reverse prefix search
Nov. 16th, 2015 01:53 pmстолкнулся с проблемой, из которой, кажется, получится хороший вопрос для интервью на продвинутого девелопера.
проблема такая: есть относительно длинная строка, например "lasdkfbvaouvyaou". и есть в базе таблица с миллиардом записей, в которой хранятся потенциальные подстроки, например "la", "las", "lasdk". надо их все найти как можно быстрее.
поверхностное гуглование нашло только кошмарное решение, гордо опубликованное на http://www.loganbibby.com/2011/03/reverse-pattern-matching-in-mysql
это решение сканирует всю таблицу, весь миллиард, вне зависимости от существования индексов.
чисто датабазное решение, которое приходит в голову, это поиск по индексу для каждой подстроки типа "l", "la", "las", и так далее. это даёт O(M log N), где M - количество букв в строке, а N - количество записей в таблице.
если надо лучше, то придётся в памяти строить trie, и делать по нему лукап. для дополнительного ускорения, вместо традиционного trie, где дети лежат в списке, можно держать детей в Dictionary, тогда будет чуть быстрее (насколько - непонятно, зависит от статистики), но памяти съест больше.
проблема такая: есть относительно длинная строка, например "lasdkfbvaouvyaou". и есть в базе таблица с миллиардом записей, в которой хранятся потенциальные подстроки, например "la", "las", "lasdk". надо их все найти как можно быстрее.
поверхностное гуглование нашло только кошмарное решение, гордо опубликованное на http://www.loganbibby.com/2011/03/reverse-pattern-matching-in-mysql
это решение сканирует всю таблицу, весь миллиард, вне зависимости от существования индексов.
чисто датабазное решение, которое приходит в голову, это поиск по индексу для каждой подстроки типа "l", "la", "las", и так далее. это даёт O(M log N), где M - количество букв в строке, а N - количество записей в таблице.
если надо лучше, то придётся в памяти строить trie, и делать по нему лукап. для дополнительного ускорения, вместо традиционного trie, где дети лежат в списке, можно держать детей в Dictionary, тогда будет чуть быстрее (насколько - непонятно, зависит от статистики), но памяти съест больше.
(no subject)
Date: 2015-11-16 10:01 pm (UTC)(no subject)
Date: 2015-11-16 10:05 pm (UTC)(no subject)
Date: 2015-11-16 10:09 pm (UTC)(no subject)
Date: 2015-11-16 10:11 pm (UTC)(no subject)
Date: 2015-11-16 10:57 pm (UTC)(no subject)
Date: 2015-11-16 11:48 pm (UTC)E.g. do we have an entry for string/2? string/4? string*3/4?
Етпочя.
(no subject)
Date: 2015-11-17 01:40 am (UTC)(no subject)
Date: 2015-11-17 02:03 am (UTC)(no subject)
Date: 2015-11-17 02:07 am (UTC)(no subject)
Date: 2015-11-17 02:19 am (UTC)(no subject)
Date: 2015-11-17 11:47 am (UTC)Из условий которые не в заголовке не уловил что всё таки надо только префиксы, я про все подстроки писал. А в чём тут реверс то возникает тогда и зачем?
(no subject)
Date: 2015-11-17 11:48 am (UTC)Если да, то можно таблицу с потенциальными подстроками переформатировать в формат, максимально совместимый с trie.
Что-то типа такого:
| ID | Character | ParentID | Value |
Тогда для строки C1-C2-C3-...-Cn
Потребуется выполнить не более N запросов.
Первый - "SELECT ID FROM TABLE WHERE Character = 'C1' AND ParentID IS NULL"
Остальные - "SELECT ID FROM TABLE WHERE Character = 'Ck' AND ParentID = <ID_FROM_PREV_QUERY>"
> если надо лучше, то придётся в памяти строить trie, и делать по нему лукап.
А разве для этого не потребуется просканировать всю таблицу?
(no subject)
Date: 2015-11-17 11:50 am (UTC)Навскидку, должно получиться что-нибудь вроде O(log M log N), но, возможно, я ошибаюсь.
(no subject)
Date: 2015-11-17 07:36 pm (UTC)(no subject)
Date: 2015-11-17 07:43 pm (UTC)> Потребуется выполнить не более N запросов.
о, отлично. это тоже M log N в моей терминологии, но основание логарифма побольше, потому что индекс уже.
> А разве для этого не потребуется просканировать всю таблицу?
ну имеется в виду, что трие строится один раз в начале, и потом отвечает на много запросов, так что это не считается.
(no subject)
Date: 2015-11-17 07:47 pm (UTC)(no subject)
Date: 2015-11-17 07:53 pm (UTC)(no subject)
Date: 2015-11-17 10:08 pm (UTC)(no subject)
Date: 2015-11-17 10:19 pm (UTC)в моей оптимизации нету половинного деления, и не понятно, как оно может помочь.
(no subject)
Date: 2015-11-17 10:47 pm (UTC)(no subject)
Date: 2015-11-17 11:10 pm (UTC)(no subject)
Date: 2015-11-18 12:37 am (UTC)Тогда задача усложняется.
Вариант А. В твоей базе нужна структура trie, в смысле, длинный префикс должен содержать пойнтер на ближайший префикс. Я так думаю. Но найти самую длинную все равно надо.
Вариант Б. Сразу trie всандалить со ссылками сверху вниз.
(no subject)
Date: 2015-11-18 01:05 am (UTC)(no subject)
Date: 2015-11-18 08:37 am (UTC)Нет. Он, конечно, log, но не N, а (N/2^i).
Trie - надо ещё создать, это O(N).