КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Поиск в строке. Алгоритм Кнута, Мориса и Пратта
Приблизительно в 1970 г. Д. Кнут, Д. Морис и В. Пратт изобрели алгоритм, фактически требующий только N сравнений даже в самом плохом случае. Новый алгоритм основывается на том соображении, что, начиная каждый раз сравнение образа с самого начала, мы можем уничтожать ценную информацию. После частичного совпадения начальной части образа с соответствующими символами строки мы фактически знаем пройденную часть строки и можем «вычислить» некоторые сведения (на основе самого образа), с помощью которых потом быстро продвинемся по тексту. Приведенный пример поиска слова «иваны» показывает принцип работы такого алгоритма. Символы, подвергшиеся сравнению, здесь подчеркнуты. Обратите внимание: при первом несовпадении двух символов образ сдвигается на все пройденное расстояние, поскольку меньшие сдвиги не могут привести к полному совпадению
КМП-алгоритм записывается так (опять используется предикат Р (1.47) и Q (1.48)) i:=1; j:=1; (1.50) WHILE (j <= M) AND (i <= N) DO BEGIN (* Q(i-j) & P(i-j, j) *) WHILE (j >= 1) AND (s[ i ] <> p[ j ]) DO j:=D; i:=i+1; j:=j+1; END; Однако такая запись умышленно не совсем точная, поскольку в ней есть сдвиг на неопределенную величину D. Вскоре мы к ней вернемся, а теперь сначала отметим, что условия Q(i-j) и P(i-j, j) сохраняются как глобальные инварианты и к ним можно добавить отношения 1 ≤ i ≤ N и 1 ≤ j ≤ M. Это предполагает, что мы должны отказаться от соглашения, что i всегда отмечает в тексте текущее положение первого символа образа. Точнее, выравненное положение образа теперь i - j. Если алгоритм заканчивает работу по причине j=М+1, то из составляющей P(i-j, j) следует справедливость P(i-М, М), т. е., согласно (1.47), совпадение начинается с позиции i - М. Если же работа окончена из-за i = N+1, то поскольку j < М+1, из инварианта Q(i) следует, что совпадения вообще нет. Теперь мы должны показать, что алгоритм никогда не делает инвариант ложным. Легко видеть, что в начале i=j=1, и он истинен. Сначала исследуем эффект от двух операторов, увеличивающих i и j на единицу. Они, очевидно, не сдвигают образ вправо и не делают ложным Q(i – j), поскольку разность остается неизменной. Но, может быть, они делают ложным P(i—j, j) - вторую составляющую инварианта? Обращаем внимание, что в этой точке истинно отрицание выражения в заголовке внутреннего цикла, т. е. либо j < 1, либо si=pj. Последнее увеличивает частичное совпадение и устанавливает P(i-j, j+1), а первое так же постулирует истинность P(i-j, j+1). Следовательно, увеличение на единицу i и j не может также сделать ложным тот или другой инвариант. Но в алгоритме остается только еще присваивание j:=D. Мы просто постулируем, что значение D всегда таково, что замена j на D оставляет инвариант справедливым. Для того чтобы найти соответствующее выражение для D, мы должны вначале понять смысл этого присваивания. При условии что D < j, присваивание соответствует сдвигу образа вправо на j - D позиций. Естественно, мы хотим, чтобы сдвиг был настолько больше, насколько это возможно, т. е. D должно быть как можно меньше. Этот процесс показан на рис. 1.1. i
j=4 D=1 j=1
Рис. 1.1. Присваивание j:=D сдвигает образ на j—D позиции вправо
Если инвариант P(i-j, j) & Q(i-j) после присваивания j значения D истинен, то перед ним должно быть истинно P(i-D, D) & Q(i-D). Это предусловие и будет поэтому нашим ориентиром при поиске подходящего выражения для D. Основное соображение: благодаря P(i—j, j) мы знаем, что si- j. … si-1 = p1 … pj-1 (мы только что просмотрели первые j символов образа и убедились в их совпадении с текстом). Поэтому условие P(i-D, D) с D ≤ j, т. е. si-D. … si-1 = p1 … pD-1 превращается в pj-D. … pj-1 = p1 … pD-1 (1.51) и предикат ~P(i—k, М) для k=1...j-D (чтобы убедиться в инвариантности Q(i-D)) превращается в pj-k. … pj-1 ≠ p1 … pk-1 (1.52) Важный вывод: очевидно, что значение D определяется одним лишь образом и не зависит от строки текста. Условия (1.51) и (1.52) говорят, что для определения D мы должны для каждого j найти наименьшее D, т. е. самую длинную последовательность символов образа, непосредственно предшествующих позиции j, которая совпадает полностью с началом образа. Для каждого j такое D мы будем обозначать через dj. Так как эти значения зависят только от образа, то перед началом фактического поиска можно вычислить вспомогательную таблицу d;эти вычисления сводятся к некоторой предтрансляции образа. Соответствующие усилия будут оправданными, если размер текста значительно превышает размер образа (М<<N). Если нужно искать многие вхождения одного и того же образа, то можно пользоваться одними и теми же D. Приведенные примеры объясняют функцию D. Примеры:
Рис.1.2. Вычисление dj
Последний пример на рис.1.2. и примеры на рис.1.3. (два его частных случая) наводят на мысль, что мы можем поступать еще лучше: так как рj равно А вместо F (рис.1.3.), то соответствующий символ строки не может быть символом А из-за того, что условие si≠рj заканчивает цикл. Следовательно, сдвиг на 5 не приведет к последующему совпадению, и поэтому мы можем увеличить размер сдвига до шести (см. рис. 1.3., верхний пример). Учитывая это, мы переопределим вычисление dj как поиск самой длинной совпадающей последовательности p1. … pdj-1 = pj-dj … pj-1 с дополнительным ограничением pdj ≠ pj. Если никаких совпадений нет, то мы считаем dj=0, что указывает на сдвиг «на целый» образ относительно его текущей позиции (см. рис. 1.3., нижняя часть).
j=6, d[6]=0; сдвиг=j-d[j]=6
j=6, d[6]=0; сдвиг=j-d[j]=6
Рис. 1.3. Сдвиг образа за последний символ
Ясно, что вычисление dj само представляет собой первое приложение поиска в строке, и мы для этого можем использовать быструю версию КМП-алгоритма. Это и демонстрирует программа 1.2, состоящая из двух частей. В первой читается строка s, а затем идут итерации, начинающиеся с чтения образа, затем следуют его предтрансляция и, наконец, сам поиск.
CONST Mmax = 100; Nmax = 1000; ESC = ЗЗС;
Дата добавления: 2014-01-13; Просмотров: 525; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |