Эффективный алгоритм сопоставления строк с очень большим набором шаблонов

Я ищу эффективный алгоритм, способный найти все шаблоны, соответствующие определенной строке. Набор шаблонов может быть очень большим (более 100 000) и динамическим (шаблоны добавляются или удаляются в любое время). Шаблоны не обязательно являются стандартным регулярным выражением, они могут быть подмножеством регулярных выражений или чем-то похожим на шаблон оболочки (например: file-*.txt). Предпочтительнее решение для подмножества регулярных выражений (как объяснено ниже).

К вашему сведению: меня не интересуют методы грубой силы, основанные на списке RegExp.

Под простым регулярным выражением я подразумеваю регулярное выражение, которое поддерживает ?, *, + , классы символов [a-z] и, возможно, логический оператор |.

Чтобы уточнить мою потребность: я хочу найти все шаблоны, соответствующие URL-адресу:

http://site1.com/12345/topic/news/index.html

Ответом должны быть эти шаблоны, основанные на шаблоне, установленном ниже.

http://*.site1.com/*/topic/*
http://*.site1.com/* 
http://*

Набор узоров:

http://*.site1.com/*/topic/*
http://*.site1.com/*/article/*
http://*.site1.com/* 
http://*.site2.com/topic/*
http://*.site2.com/article/*
http://*.site2.com/* 
http://*

person lquerel    schedule 31.01.2013    source источник
comment
Будет ли ваш набор шаблонов всегда относиться к URL-адресам или это просто пример?   -  person Will A    schedule 31.01.2013
comment
Я бы сделал реализацию грубой силы, чтобы служить ориентиром и измерить эффективность (и точность) ваших альтернативных подходов к этому.   -  person John Sobolewski    schedule 31.01.2013
comment
Не могли бы вы уточнить, какую грамматику регулярных выражений вы имеете в виду?   -  person    schedule 31.01.2013
comment
Под простым регулярным выражением я подразумеваю регулярное выражение, которое поддерживает ?, *, + , классы символов [az] и, возможно, логический оператор |   -  person lquerel    schedule 31.01.2013
comment
Спасибо @jsobo за это предложение. Очень признателен!   -  person lquerel    schedule 31.01.2013
comment
Если вам нравятся ответы, которые вы видите, вы должны проголосовать за них.   -  person Ira Baxter    schedule 01.02.2013
comment
Конечно, я сделаю это, когда моя оценка репутации будет больше 15. Недавно я открыл свою учетную запись stackoverflow.   -  person lquerel    schedule 03.02.2013


Ответы (3)


Вот подход, который мы довольно успешно использовали (реализация здесь):

Добавление шаблонов:

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

dog*fish -> [dog, fish]
[lfd]og  -> [og]
dog?     -> [dog]

Когда вы добавляете шаблон в структуру данных, разбивайте его на метаслова и сохраняйте их в структуре данных, соответствующей строке Ахо-Корасика. Поддерживайте внутреннюю структуру данных, чтобы сопоставлять метаслова со словами-шаблонами.

Выполнение запросов:

Получив входную строку, используйте созданную вами структуру данных Ахо-Корасика, чтобы получить все метаслова, содержащиеся в этой строке. Затем, используя созданную вами карту, проверьте шаблоны, соответствующие этим метасловам.

Это хорошо работает, потому что, хотя сопоставление строк происходит довольно медленно, вы можете очень быстро сузить количество шаблонов, с которыми вам действительно нужно сопоставляться. Наша реализация может выполнять около 200 000 запросов в секунду на обычном ноутбуке по наборам из более чем 150 000 шаблонов. См. режим бенчмаркинга в программе, чтобы проверить это.

person Zeke Medley    schedule 13.04.2019

Один из подходов, который приходит на ум, заключается в создании древовидных структур шаблонов.

Пример: http://* будет содержать все шаблоны (перечисленные выше). http://*.site1.com/* будет содержать все site1.com. Это может значительно сократить количество шаблонов, которые необходимо проверить.

Кроме того, вы можете определить, какие шаблоны являются взаимоисключающими, чтобы еще больше сократить список, который вы ищете.

Итак, сначала возьмите все шаблоны и создайте из них деревья. Выполните поиск по всем корням, чтобы определить, какие ветви и узлы необходимо проанализировать.

Улучшите алгоритм, определив, какие ветки являются взаимоисключающими, чтобы, обнаружив попадание в данную ветку, вы знали, какие ветки/узлы не нужно посещать.

Для начала вы можете быть ленивым, и ваш первый проход может состоять в том, чтобы отсортировать шаблоны и сделать простой следующий шаблон, содержащий логику этого типа шаблона, чтобы определить, содержится ли «это» в следующем. ПРИМЕР: if( "http://*.site1.com/*".startsWith("http://*") == true )

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

Чтобы лучше определить вопрос:

«Этот шаблон содержит этот шаблон?»

Я считаю, что вам нужно будет разобрать регулярное выражение... Эта статья выглядит хорошим местом для начала, чтобы понять, как это сделать: Разбор регулярных выражений с рекурсивным спуском

person John Sobolewski    schedule 31.01.2013
comment
Должно быть достаточно и эффективно для соответствия шаблонам оболочки, но его сложнее применить для простого регулярного выражения. Если для простого регулярного выражения не существует решения, я воспользуюсь этим подходом. Я завершил свой вопрос, чтобы уточнить термин «простое регулярное выражение». Спасибо - person lquerel; 31.01.2013
comment
это сложно из-за таких случаев: ...dont[1234].* и ...don[et][345] и ...don[et][23456] очевидно для человека, который содержится внутри другие, однако, запрограммировать это немного сложно. - person John Sobolewski; 01.02.2013

Если набор URL-адресов не меняется очень быстро, вам действительно следует использовать механизм регулярных выражений, который компилирует его шаблоны. Java предоставляет один из них, но он может быть неудовлетворительным, если вы хотите знать, какой шаблон соответствует.

Широко используемый механизм для выполнения этого и определения совпадения — это различные генераторы лексеров, например, FLEX и подобные инструменты. Они принимают количество регулярных выражений для каждой «лексемы» и создают интегрированный FSA для распознавания любого из них, который чрезвычайно эффективен для выполнения.

Вы можете вызвать Flex, когда ваш набор изменится. Если это слишком медленно, получите версию Flex с открытым исходным кодом и интегрируйте ее в свой движок; он внутри создает FSA, поэтому вы можете использовать его напрямую. (вероятно, потребуется некоторая инженерия). Но если у вас действительно есть проблема сопоставления с высокой производительностью, некоторая работа, чтобы сделать это хорошо, не будет вас беспокоить.

Если набор URL-адресов меняется быстрее, чем FLEX может генерировать FSA (странно), у вас есть реальная проблема. В этом случае вы можете построить онлайн-дерево дискриминации, сканируя «регулярное выражение» слева направо и интегрируя символы/предикаты, которые вы видите, в ваше существующее дерево дискриминации. Затем сопоставление состоит из спуска по дереву различения, выполнения различных тестов; если вы дойдете до листа, у вас есть совпадение, иначе нет. Это может быть так же быстро, как автоматизация, созданная FLEX, если все сделано правильно, но, вероятно, намного, намного больше.

person Ira Baxter    schedule 01.02.2013
comment
Во-первых, спасибо за этот интересный ответ. Да, я хотел бы знать шаблоны, соответствующие URL-адресу. Наконец, для шаблонов-кандидатов выполняется постобработка для выбора лучшего шаблона. Таким образом, решение, основанное на большом регулярном выражении, невозможно IMO. Я протестирую решение на основе Flex на JFlex с шаблонами 100K, чтобы проверить осуществимость/производительность этого подхода. На следующей неделе отпишусь о результате. - person lquerel; 01.02.2013
comment
Результаты не очень хорошие... Время преобразования NFA в DFA слишком велико для CPU+Memory, если количество шаблонов слишком велико. Я протестировал JFlex для создания сканера для 10000 шаблонов и всегда жду результата (даже с опцией --nomin). То же наблюдение, когда я использую такую ​​библиотеку, как brics.dk/automaton. - person lquerel; 04.02.2013
comment
Какие цифры производительности вы видите? Пытливые умы хотят знать. 0) Кого волнует память? 8 ГБ оперативной памяти — 100 долларов США; 1) Я бы проверил gnu Flex (быстрый лексический анализатор). Повидал много миль и лет, вероятно, имел серьезную полировку производительности. 2) Вы всегда можете запустить это в фоновом режиме, живя с текущим лексером, пока не будет завершена новая версия. 3) альтернатива, дискриминационная сеть, также, вероятно, будет иметь большой объем памяти, поскольку это дерево со 100 000 путей и листьев, и вы теряете выразительность регулярных выражений. - person Ira Baxter; 04.02.2013
comment
Что ж, теперь мои тесты основаны на FLEX, и результаты, к сожалению, не лучше. 32s для создания lex.yy.c для 800 шаблонов, а FLEX отклоняет входной файл с 900 шаблонами (сообщение об ошибке: правила ввода слишком сложны (›= 32000 состояний NFA)). Я пытаюсь изучить решение, основанное на троичном дереве поиска. - person lquerel; 04.02.2013
comment
Похоже, что FLEX не оправдал своей репутации. Как быстро меняется набор URL-адресов (сколько меняется в день?). Готовы ли вы прислать мне свой (реалистичный) образец из 100 000 шаблонов? У меня есть лексер, я хотел бы попробовать это. Смотрите мою био для контактных данных. - person Ira Baxter; 04.02.2013
comment
В порядке. Есть ли у вас предпочтения по формату узоров? Гибкий формат? - person lquerel; 04.02.2013
comment
Формат Flex подходит. Я могу легко преобразовать их в нужный мне формат. - person Ira Baxter; 04.02.2013
comment
Отправил. Заранее спасибо. - person lquerel; 05.02.2013