#java #regex #string #algorithm #substring
#java #регулярное выражение #строка #алгоритм #подстрока
Вопрос:
Я хочу понять проблемы с производительностью, которые могут возникнуть при выполнении поиска по подстроке в Java. Я знаю два встроенных метода поиска подстроки в Java.
1. String.indexOf()
Насколько я понимаю, этот метод использует алгоритм перебора поиска подстроки, таким образом, его сложность равна O (nm), где n и m — длины строки и шаблона.
2. Используйте шаблон и средство сопоставления
Я ничего не знаю о том, как реализованы алгоритмы регулярных выражений и об их сложности.
Итак, вопросы:
1) Какой из этих методов предпочтительнее с точки зрения производительности?
2) В чем сложность поиска по регулярному выражению? Зависит ли это от самого регулярного выражения?
Комментарии:
1. Aho-Corasick — лучший выбор, если вас действительно беспокоит скорость
2. Доказано, что оптимизация по Бойеру-Муру в худшем случае выполняется за линейное время. Конечно, такого рода … противоречит цели вопроса с точки зрения того, что представлено. Вам нужен самый быстрый способ поиска подстроки с использованием только этих инструментов? Какие типы подстрок вы ищете? Не могли бы вы привести примеры ввода и ожидаемого результата?
3. Сложность поиска по регулярному выражению сильно варьируется в зависимости от того, что сопоставляется. В большинстве случаев оно совпадает очень быстро, но из-за обратного отслеживания на неудачное совпадение уходит много времени.
4. Я бы выбрал indexOf() вместо регулярного выражения, когда это возможно.
5. Причина
indexOf
использования этого алгоритма в том, что в большинстве случаев он самый быстрый на практике, даже если теоретически другие алгоритмы могут иметь лучшую асимптотику.
Ответ №1:
Честно говоря, если вас волнует производительность в наихудшем случае, используйте JNI в машинном коде, который вызывает strstr
функцию вашей стандартной библиотеки. Хорошо реализованный strstr
, как и в последних версиях glibc, имеет линейное время выполнения в наихудшем случае и постоянное использование пространства в наихудшем случае. Я полагаю, что glibc strstr
также может выполнять длинные переходы по тексту, подобные Boyer-Moore. Стандартные библиотеки C поддерживаются людьми, которые знают, как писать и поддерживать хорошие библиотеки общего назначения, и практикуются в своем ремесле. Этого нельзя сказать о стандартной библиотеке классов Java.
Вам нужно будет превратить строку Java UTF-16 во что-то подходящее для strstr
, например, строку UTF-8. Вам также придется корректно обрабатывать встроенные нулевые байты в строке UTF-8. Кроме этого, вы будете пользоваться преимуществами хорошо написанной и поддерживаемой библиотеки.
Java выполняет поиск по регулярным выражениям (для данного конкретного случая) с использованием поиска по строке Бойера-Мура, взломанного в наивной реализации регулярных выражений. Компиляция a Pattern
только с вашей строкой приведет к Matcher
относительно хорошей производительности. Однако обратите внимание, что это НЕ распространяется ни на что, кроме поиска строк с помощью библиотеки регулярных выражений; вы по-прежнему придерживаетесь наивной реализации регулярных выражений, которая выполняет обратный поиск, если вы вводите в нее нетривиальное регулярное выражение.
В качестве доказательства того, почему вы не должны использовать Java regex для реальных регулярных выражений, я представляю вам следующее:
public class regex {
public static void main(String[] args) throws Exception {
String haystack = "ab";
String needle = "abab?.*";
for (int i = 0; i < 7; i ) haystack = haystack haystack;
for (int i = 0; i < 4; i ) needle = needle needle;
System.out.println(haystack.length() " " needle.length());
long before = System.currentTimeMillis();
System.out.println(Pattern.matches(needle, haystack));
long after = System.currentTimeMillis(); // long after indeed...
System.out.println(after - before);
}
}
Это поиск в 256-символьном стоге сена регулярного выражения needle (это честное регулярное выражение, о котором вы узнали в классе compilers) из 112 символов. На моей машине для завершения требуется около 24 секунд.
Комментарии:
1. @Unihedron: На самом деле этого не произойдет. Возьмите любую книгу по вводным компиляторам и прочитайте о том, как реализовать регулярные выражения. Или посмотрите на реализацию регулярных выражений Расса Кокса на swtch.com /~rsc/regexp .
2. Знаете какие-нибудь движки регулярных выражений получше? Perl 5.10 занимает на 40% больше времени, чем Java 1.8.
3. @laune: На странице Расса Кокса есть указатели на четыре реализации. Я лично использовал библиотеку Russ re2. Вы можете убедиться, что это работает правильно, на примере, который я привел.
4. тмыклебу, я просто не могу понять из твоего ответа — каким способом быстрее искать точное соответствие подстроки: регулярным выражением или методом перебора или каким-то алгоритмом вроде KMP или Бойера-Мура, хорошо реализованным?
5. Я полагаю, что No1 — это какой-то алгоритм, ок, который не является вариантом 2, если я не хочу ничего кодировать самостоятельно?