Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
О.О.П / ооп / 4_кол / К курсовой / Методи побудови алгоритмів та їх аналіз / Інформатика_1 (методи побудови алгоритмівта та їх аналіз).doc
Скачиваний:
0
Добавлен:
30.05.2020
Размер:
2.5 Mб
Скачать

Скінченні автомати Основні поняття

Теорія автоматів - це один із розділів інформатики. Якщо бути точнішими, то теорія автоматів належить до теоретично! інформатики і вивчає методи, за допомогою яких можна на ос-нові моделей логічного типу вивчати процеси, що протікають у різних пристроях під час обчислень.

Почнемо з уведення загальних понять теорії автоматів.

У широкому тлумаченні автоматами називають пристрої, які виконують процеси приймання, перетворення та передачі енергії, матеріалів або інформації відповідно із закладеними у них програмами.

В інформатиці під автоматом розуміють перетворювач ін-формації, для якого задаються множини вхідних сигналів А, внутрішніх станів Q, вихідних сигналів V, а також дві функції: функція переходів станів 6 та функція виходів X.

Схематично роботу автомата можна зобразити так (мал. 42):

Q

w

^

Мал. 42

I ще нам треба ввести такі два поняття: скінченний вхідний алфавіт, який насправді є множиною елементів, над якими може виконуватися перетворення даним автоматом, напри-клад множина натуральних чисел або латинські літери, та сло­во - підмножина алфавіту, над якою безпосередньо і відбу-вається перетворення, тобто яку обробляє автомат у даному

127

конкретному випадку, налриклад задане натуральне число N або деякий текст латинськими літерами. Іншими словами, «слово» - це вхідна інформація.

Якщо введет нові поняття здаються вам складними, то на-справді це зовсім не так. Розберемося з ними детальніше, і все стане напрочуд зрозумілим. Згодом навіть виявиться, що ми вже користувалися алгоритмами, які реалізують скінченні автомати. Термін «скінченний» пояснимо, коли детальніше на прикладах ознайомимося з поняттям автомата.

Для кращого розуміння, почнемо з формулювання задачі, розв'язування якої ми розглянемо в термінах автомата. Нехай задана послідовність натуральних чисел а[і], і - 1, 2, ..., п, у якій треба всі числа, що перевищують 7, замінити на 7.

Завданням автомата є обробка за певною програмою інфор-мації, яка подається йому у вигляді «слова». У нашому випад­ку таким «словом» є задана послідовність дійсних чисел. «Сло­во» складається з окремих елементів а - заданих натуральних чисел, що входять до множини вхідних сигналів А, тобто до множини натуральних чисел. Функція переходів визначає, в який стан q' із множини можливих станів Q перейде перетво-рювач, якщо він перебувае у стані q i на вхід надійшов сигнал а: б(<у, а) = q'. У термінах нашої задачі це означає, що коли на деякому кроці в послідовності значень а[і] буде оброблятися число, значения якого перевищує 7, то воно буде замінено на 7. Тобто при обробці числа а[і] > 7 спрацює функція переходу 6: а[і] :- 7 і перетворить число, яке більше за 7 (вхідний сигнал), на число 7. Зауважимо, що q' також входить до множини Q.

Функція виходів у результат! виконання функції перетво-рення показує, який при цьому виробляється вихідний сигнал v, що належить множит вихідних сигналів V: k(q, а) = v. У нашому прикладі вихідних сигналів буде два: один - про-довження введения дійсних чисел у випадку, коли і < п, і завер­шения введения, коли і > п. Перший позначимо, наприклад, О, другий - I.

Стандартним описом скінченного автомата є таблиця, яку називають таблицею перетворень.

Отже, представимо описаний скінченний автомат, що реа-лізує поставлену задачу у вигляді такої таблиці переходів (табл. 11): у рядках розмістимо можливі стани виконання ал­горитму - перехід до наступного елемента заданої послідов-ності та заміну поточного елемента на 7; у стовпчиках — усі можливі значения елементів послідовності. На перетині відпо-відних рядків і стовпчиків розміщене посилання на функцію переходу, яку описує даний елемент таблиці. Наприклад, якщо поточний елемент послідовності дорівнює 2, то переходимо до розгляду наступного елемента, якщо ж 8, то спочатку робимо

128

заміну його значения на 7, а потім знову переходимо до розгля-ду наступного елемента. Алгоритм завершиться, коли припи­ши-ься введения елементів послідовності.

Таблицяіі

^~~~\^^ Вхіда, Стан q, \^^

1

2

...

6

7

8

...

?,(і := і + 1)

Яг

92

Яг

92(а[і]:=7)

Ь

аі

Тепер можна визначитися щодо назви «скінченні автома­та». Справа в тому, що у нашому випадку всі множини сигна-лів А та V і внутрішніх станів Q дискретні, тобто порційні, а їх елемента ізольовані один від одного: послідовність вхідних дійс-них чисел (а[ї\, і - 1, 2, ..., п), перетворення значень деяких із них за умовою задачі (якщо а[ї] > 7, тоді а[і]:- 7), завершения виконання задачі (і > п). Дискретною є і послідовність момен-тів, у які надходять вхідні сигнали, отримуються вихідні та змінюються внутрішні стани: перехід до введения наступного значения а[ї\ відбувається лише після завершения обробки попереднього.

Будемо говорити, що якщо множини сигналів і станів скін-ченні, то автомат, обо перетворювач, називається скінченним.

Наведемо означения скінченного автомата в термінах алго-ритмізапії, оскільки саме цей розділ інформатики знаходиться в полі нашого зору і саме методами побудови алгоритмів ми займаємося.

Скінченним автоматом будемо називати алгоритм, у якому передбачено обробку скінчешюї кількості всіх можливих ситуацій та переходу вхідної інформації за скінченну кіль-кість кроків із заданого початкового стану в кінцевий.

Тепер, коли поняття скінченного автомата стало досить про-зорим, можна навести чимало прикладів із тих задач, які ми раніше розв'язували, і тепер сформулювати їх у нових термі-нах. Наприклад, задача, яка передбачає введения недодатних значень для змінних, що можуть набувати лише натуральних чисел; контроль за перемиканням клавіатури на ту чи іншу абетку при введенні текстової інформації тощо.

Розглянемо ще кілька задач, алгоритми яких можна пред-ставити у вигляді скінченних автоматів.

■ ) Ыфораттщгя 9 l»11 і

129

Нехай треба визначити правильність розміщення круглих дужок у заданій математичній формулі. Для спрощення вва-жатимемо, що вхідна інформадія задається у вигляді тексту, який містить натуральні числа, знаки арифметичних операцій (+, -, •, /) і відкриті та закриті круглі дужки.

Алгоритм визначення коректності розставлених круглих дужок в арифметичному виразі полягає в покроковому збіль-шенні лічильника k у випадку «(» та його зменшення у випад­ку «)» . Якщо протягом виконання алгоритму виконається умо-ва k < О, то це означатиме, що порушено баланс відкритих та закритих дужок і далі виконання алгоритму не мае змісту. Якщо перегляд вхідної інформації завершено, то ознакою ко-ректності розміщення круглих дужок в арифметичному виразі є виконання умови k »= 0.

Які ситуації нам потрібно обробляти, тобто які внутрішні стани можуть бути? Перше - це перехід до наступного символу у випадку, коли він не є дужкою, та перевірка завершеності введения інформації. Друге - підрахунок відкритих круглих дужок, тобто збільшення лічильника ft на 1. Третє — підра-хунок закритих круглих дужок, тобто зменшення лічильника k на 1. Четверте — перевірка співвідношення k < 0, тобто кон-тролювання ситуації, коли закритих дужок стане більше, ніж відкритих. П'яте - завершения роботи автомата. Робота такого автомата представлена у вигляді таблиці 12.

Таблиц* 12

^-. Вхід а, Стенд, ^--^

0

1

...

9

+

-

/

(

)

q, (перехід до на­ступного символу при і< п)

4v 0/1

7,.

o/i

9ii

0/1

4v

0/1

9,. 0/1

9v

0/1

7i'

o/i

7,. 0/1

72. 0/1

78. 0/1

д., (збільшення лі-

чильника к для

«(» на 1)

?4.0

<74.0

7,-0

7,-0

g4.o

<7,. 0

7..0

7„0

7..0

74>0

<73 (зменшення ЛІ-

чильника k для

«)» на 1)

?4.0

?4'0

g4,o

«/4,0

94

?4.0

?4.0

74-0

74.0

74-0

qt (Error: k < 0)

4v 1/0

4v 1/0

<7,. 1/0

4v

I/O

1v 1/0

Si

4v I/O

7p I/O

7,-1/0

7,. I/O

qb (Stop: к - 0)

Я,Л

?5. 1

<75. 1

9e.l

Ї..1

75.1

95.1

75.1

75,1

75.1

130

У комірках на перетині відповідних станів та елементів вхідної інформації будемо вказувати дві величины: значения функції переходу 6(9,, а.) та через кому значения функції вихо-ду X(q., cif) (табл. 12).

Значениям функції переходу 6 може бути одне з можливих значень, указаних у переліку станів qr Значениям функції вы­ходу К може бути 0 (робота автомата продовжуеться) та 1 (робо­та автомата припиняється).

Для зручності значения функції виходу будемо вказувати так. Якщо результат цієї функції однозначний, то будемо вка­зувати одне це значения, якщо ж значень може бути два, за-лежно від виконання умови, яка аналізується, то будемо вказу­вати два значения: перше - для випадку виконання умови, друге - для випадку ЇЇ невиконання. Наприклад, qv 0/1 озна­чав перехід до стану qi зі значениям функції виходу 0 у разі виконання умови, що аналізується в цьому стані (це означав, що робота автомата продовжуеться), та 1 у разі невиконання умови (робота автомата припиняеться).

Отже, ми представили алгоритм, описаний вище, в іншій формі - у вигляді таблиці переходів скінченного автомата.

Змінимо попередню задачу і будемо контролювати корект-ність написания імен функцій, використання яких передбаче-но умовою задачі. Для спрощення запису таблиці переходів створимо скінченний автомат лише для функції синуса і буде­мо вважати, що символи «s», «i» та «n» використовуються ли­ше у выклику вказаної функції. Побудуємо таблицю переходів для цієї задачі (табл. 13).

Таблиця 13

^\^ Вхід а, Станд, ^--^

0

1

...

9

+

-

s

І

п

ql (перехід до на-

ступного символу

при і < N)

9,> 0/1

9,. 0/1

0/1

4v

0/1

9v

0/1

9p 0/1

9i> 0/1

Яг> 0/1

Я,Л

9,.1

q2 (иерехід до на-

стушюго символу

при і < N і

перєвірка

символу «і»)

Яі'1

92. 1

Яг'1

92. 1

Яг< X

qv 1

ЯгЛ

ЯгЛ

я3>

0/1

7,.1

q3 (перехід до на-

ступного символу

при j < N і

перевірка

символу «11»)

я3Л

98. 1

g3.i

9з»1

9,.l

Ч,Л

я3Л

Я3Л

9а»1

9і-0/1

R

131

Мабуть, варто пояснити деякі значения функцій переходів у наведеній таблиці.

Для стану gt характерно те, що введения будь-якого сим­волу, крім «s», «і» та *п», призводить до переходу до цього самого стану д, із значениям функції виходу 0, коли введения інформації ще не завершено, та 1, коли завершено. У випадку введения символу «s» перехід відбувається лише до стану q2 із значениям функції виходу 0, тобто введения продовжується далі, або ж 1, коли цей символ є останнім. У випадку введения символів «і» та «п» виробляеться значения функції виходу 1, і робота автомата припиняється, оскільки за умовою задачі ці символ и не можуть передувати символу «s».

Для стану q2 введения будь-якого символу, крім «і», є по­милковим, тому значения функції виходу дорівнює 1 і перехід до іншого стану не мае ніякого значення, а при введенні сим­волу «і» перехід відбувається до стану q3 відповідно із значен­иям функції виходу 0, коли введения символів продовжується, і 1, коли припиняється.

Стан q3 передбачає перехід до стану g, лише при введенні символу «п» відповідно із значениям функції переходу 0/1, а в усіх інших випадках функщя виходу дае значення 1, тобто будь-яке значення символу, крім «п», є помилковим, і робота автомата припиняється.

Як і в попередньому прикладі за такою таблицею переходів нескладно розробити алгоритм, що контролює правильність за-пису функції синус в математичному виразі.

Спробуємо відповісти на запитання: коли варто формулю-вати алгоритми у термінах скінченних автоматів? Зрозуміло, що коли алгоритм є досить прозорим і в ньому небагато пере-творень, то у створенні таблиці переходів немає потреби. Якщо ж умова задачі передбачає багато перетворєнь або ж ці пере-творення є досить склад ними, залежними одні від одних, то зручніше побудувати таблицю переходів і при цьому реалізація алгоритму стане набагато прозорішою.

А зрештою, використання скінченних автоматів треба роз-глядати як один із підходів до побудови алгоритмів.

Слушним є запитання, чому елементи теорії автоматів ми розглядаємо в розділі пошукових алгоритмів. Справа в тому, що деякі алгоритми, які можна представити у вигляді таблиці переходів, і є пошуковими. Адже задачі, які ми розглянули, підтверджують це.

Запитання для самоконтролю

  1. Які питания інформатики вирішує теорія автоматів?

  2. Як тлумачиться в інформатиці поняття «автомат»?

  3. Які основні поняття входять до формування поняття «автомат»?

132

  1. Зобразіть схематично роботу автомата.

  2. Чому автомати, які розглядаються в інформатиці, називаються скінченними?

  3. Дайте означения сличенного автомата в термінах алгоритмізації.

  4. Наведіть приклад задачі та сформулюйте її в термінах теорії ав-томатів. Побудуйте для неї таблицю переходів і функції виходу.

  5. Обґрунтуйте значения таблиці переходів у наведеному в пункті 7 прикладі.

  6. Коли варто формулювати алгоритми у термінах скінченних ав­томате?

Завдання

  1. Реалізувати у вигляді алгоритму побудований скінченний автомат для задачі заміни на 7 усіх значень, що перевищують 7 у заданій послідовності натуральних чисел а[і], I = 1, 2,..., п.

  2. Протестувати алгоритм, реалізований у завданні 1.

  3. Реалізувати у вигляді алгоритму побудований скінченний автомат для задачі перевірки правильності розставлення круг-лих дужок в математичному виразі.

  4. Протестувати алгоритм, реалізований у завданні 3.

  5. Реалізувати у вигляді алгоритму побудований скінченний автомат для задачі перевірки правильності визначення імені функції синус в математичному виразі.

  6. Протестувати алгоритм, реалізований у завданні 5.

Пошук підрядків за допомогою скінченних автоматів.

КМП-пошук

Цей метод уперше описано Д. Кнутом, Д. Морісом і В. Прат-том у 1970 р. Його особливістю є те, що він базується на ство-ренні функції перетворень.

Сформулюємо спочатку задачу, яку будемо розв'язувати. Нехай треба знайти перше входження підрядка х, який скла-даеться з т символів, у рядок s, у якому міститься п символів < п).

Подібну задачу ми вже розглядали, але ЇЇ розв'язування зво-дилося до лінійного пошуку, тобто ми покроково зміщували підрядок х у рядку s, перевіряючи збігання їх символів. Такий метод мае право на існування, але він не є досить ефективним, оскільки вимагає п ■ т порівнянь збігання символів.

Розглянемо інший підхід до пошуку розв'язання поставле­но! задачі.

Почнемо порівнювати символ и підрядка х - х[1] ... х[т] із символами рядка s - s[l]... s[n] із початку. Нехай s[l] - х[1],..., «[/-!] = хЦ ~ 1]. s[j] * x\j], де / < т (мал. 43).

133

Ііадалі на малюыках ті елементи підрядка х і рядка s, що збі-гаються, будемо позначати світло-сірим кольором, а ті, що не збігаються, — темно-сірим.

s[l]

e[i]

...

e[y-l]

*w

s[ml

...

8[д]

41]

x[l]

...

«0-1]

*M

...

x[m]

Мал. 43

Зрозуміло, що підрядок не входить у рядок із першої пози-ції, оскільки відбулося його незбігання у ;'-й позиції. Можна, звичаино, спробувати почати перевірку з другої за порядком позиції у рядку, як це робилося раніше при лінійному пошуку, але виявляеться, так діяти зовсім не обов'язково.

Розглянемо такий приклад: х = 'ababb' i s = 'ababababbab1 (мал. 44).

123456789 10 11

a

b

a

b

a

b

a

b

b

a

b

a

b

a

b

Ь

12 3 4 5

Мал. 44

3 малюнка видно, що після того як виявилося, що s[5] = 'а' * * 'Ь' = х[5], є сенс починати наступну перевірку лише з s[3], ос-кільки саме там є наступне входження початку підрядка х. Можна також помітити, що символами s[3]s[4] - 'ab' водночас закінчується й починається частина тдрядка лг[1]л:[2]д:[3]д:[4]-Тому можна також казати, що наступне входження підрядка х можливе, коли д:[1]х[2] займуть місце дг[3]х[4] (мал. 45). Таким чином, підрядок «зсунеться» відносно рядка відразу на дві по-зиції'!

123456789 10 11

а

Ь

а

Ь

а

Ъ

а

Ь

ъ

а

Ъ

а

Ъ

а

b

Ъ

а

ь

а

Ъ

Ь

.

12 3 4 5

Мал. 45

134

Тепер виявляється, що s[7] * х[5] і підрядок х можна знову зсунути відразу на дві позиції, щоб х[1]х[2] знову зайняли міс-це д:[3]дг[4], збігаючись при цьому з s[5]s[6] (мал. 46).

123456789 10 11

а

b

a

b

a

b

a

b

b

a

b

а

b

a

b

b

a

b

a

b

b

a

b

a

b

b

12 3 4 5

Мал. 46

Тепер s[7] = x[3], s[8] ■ x[4], s[9] = x[5] i входження, почи-наючи з позиції 5, знайдено.

Спробуємо узагальнити помічені особливості при відшукан-ні позиції зміщення підрядка х у рядку s.

Отже, нехай перевіряється входження зразка від позиції і — j, х[1] ... х[і] = s[i;']... s[i1], a x\j + 1] не збігається з чер-говим символом рядка s[i] (мал. 47).

s[l]

...

s[i-j]

...

S[/-1]

s\i]

s[l+l]

...

x[l]

...

«M

x[j+l]

xU+2]

...

Мал. 47

Виділимо i розглянемо окремо ту частину рядка s і цідряд-ка х, які збіглися (мал. 48).

Ф-Л

...

ep-1]

41]

...

АП

Мал. 48

Нагадаємо, що в розглянутому вище прикладі х = 'ababb' і s = 'ababababbab' ми намагалися відшукати найоптимальніший зсув нашого підрядка у рядку і такий зсув знайшли, змістивши на другому кроці наш підрядок х зразу на дві позиції у рядку s. Таким чином, ми помітили, що при такому зсуві перші два еле-менти підрядка збігаються з кінцем тієї її частини, що збіглася

135

за значениями з елементами рядка (див. мал. 45): х[1]д:[2] ■ = х[3]*[4] ('ab' - 'ab').

Узагальнюючи наведений приклад, можна дійти висновку, лцо в загальному випадку, коли збіглися частини шдрядка х[1] ... x\j] iрядкаs[i — /]... s[i - 1], требавідшукатитакий най-довший початок х[1]... x[k] підрядка, який водночас є і кінцем фрагмента підрядка х[1]... x[f], що у цій частині з деякої пози-ції збігається і з фрагментом заданого рядка s (мал. 49).

s[i-j]

...

s[i-j-k+l]

...

s[i~k+l]

...

s[i-l]

х[1]

...

x[k]

...

xV-k+1]

...

x[l]

...

x[k]

Мал. 49

Це можна пояснити так:

- по-перше, зсув підрядка по рядку може відбуватися лише в межах тієї частини х, що збіглася, тобто у проміжку від 1-го до у-го елемента;

- по-друге, при такому зсуві деякий початок підрядка х (перші k елементів) накладаємо на таку саму кількість його ос- танніх j елементів, і вони при цьому повинні збігатися.

Шдіб'ємо підсумок наведених вище міркувань: перехід від перевіреного початку підрядка довжини ;' до перевіреного по­чатку довжини k (1 < k < j) означав зсув підрядка відносно ряд­ка s відразу на jI — k позицій. Але на меншу кількість позицій зсувати підрядок немає сенсу, оскільки х[1]... х[Щ - це найдов-ший початок підрядка х, що збігається з кінцем фрагмента рядка s[l] ... s[i - 1].

Продовжимо наші міркування далі. Збігання елементів х[1] ... x[k] з елементами s[i - j - к + 1] ... s[i - 1] не припиняє процес порівняння далі, оскільки елементи підрядка х ще не вичерпано. А саме, якщо x[k + 1] - s[i], то можна продовжува-ти порівняння від символу s[i + 1]. Якщо x[k + 1] X s[i], то треба відшукати найдовший початок х[1] ... x[AJ підрядка, що збі-гається з кінцем х[1] ... x[k] (і з кінцем s[l]... s[i - 1]), і порів-няти x[kx + 1] із s[i]. Описаний алгоритм потрібно застосовува-ти далі аналогічним чином.

Наприклад, якщо s - 'abababc', ал» 'ababc', то за спроби «прикласти» підрядок, починаючи з першого символу рядка, маемо х[1] - s[l], х[2] - в[2], х[3] = в[3], х[4] = s[4], х[5] * s[5], тобто j = 4. Відповідним значениям k буде 2, оскільки 'ab' є най-довшим початком рядка 'abab', що є водночас його кінцем. Звідси випливає, що немає сенсу пробувати «прикласти» під-

136

рядок до рядка, починаючи з його другої позиції, а слід «пе­ресунута» його відразу на у' — k = 2 позиції. При цьому гаран-тується рівність х[1] ... x[k] i s[ik] ... s[i - 1], тобто назад від позиції s[i] у рядку можна не повертатися.

Отже, можна зробити висновок, що для заданого підрядка х існує строго визначена залежність і вона полягає в такому: для кожної позиції у підрядка відома найбільша довжина f(j) < j такого початку підрядка x[l] ... x[f(j)], що збігається з кінцем х[1] ... x{j].

Оскільки підрядок х задається на самому початку алго­ритму пошуку і не змінюється протягом виконання пошуково-го алгоритму, то можна для кожної позиції у (1 < у < т) задано­го підрядка визначити такі значения k (1 < k < у), для яких х[1] ... x[k] збігається з x{j - k + 1]... x\J]. Значения k i буде ca-ме тим зсувом, який повністю задовольняє оптимальні вимоги пошуку входження підрядка х у рядок s. Значения функції f(j) = k для l<ft<y<mie фактично функцією переходу при кожному конкретному стані розгляду фрагмента підрядка д:[1] ... x[j], де 1 <у'< т.

Перейдемо до алгоритму побудови функції переходу f(j), яка виражає довжину такого найдовшого початку рядка х[і]... x[j], що є водночас його кінцем. У літературі така функція ще нази-вається функцією відступів для методу КМП-пошуку.

Очевидно, що Д1) = 0. Далі міркуємо так. Нехай усі значен­ия /(1),..., f(j - 1) уже обчислено, причому ВІДОМО, що f(j -l) = k, тобто у випадку збігання х[1] ... x[j - 1] із s[i - у - 1] ... s[i - 2] наступний зсув у рядку треба зробити на у - k елементів віднос-но s[ij1] (мал. 50).

"p-jrij

...

s[i-j-k]

...

s[i-k]

...

s[i-2]

x[l]

...

x[k]

...

x[j-k]

...

x\j~r\

x[l]

...

x[k]

Мал. 50

Розглянемо тепер, яким може бута значения /(у). Нага-даемо, як треба тлумачити значения /(у). По-перше, це означав, що ми продовжили розгляд фрагмента підрядка від його почат­ку ще на один елемент: х[1]... x\j]. Але якщо розглянути попе-редне «прикладання» х[1] ... x[k] до x\j - k] ... x[j - 1], яке ми визнали таким, що збігається, то тепер треба провести порів-няння наступних двох елементів: x[j] i x[k + 1] (мал. 51).

137

s[i-j-l]

...

s[i-j-k]

s[i-j-k+l]

...

s[i-k]

...

Sp-2]

s[i-l]

x[l]

...

x[k]

x[k]

...

xV-k]

...

x[j-l]

m

x[l]

...

x[k]

x[k+l]

Мал. 51

Тут можливі два варіанти.

Якщо x[j] = x[k + 1], то кінець рядка х[1] ... x[j - l]x\j] збі-гається з його початком довжини ft + 1, тому логічно, що f(j) =

= Г0-1) + 1 = л + 1.

Якщо ж x[j] =■= x[k + 1], то треба спробувати підібрати інший такий фрагмент х[1]... x[ftl], що х[1]... x[ftj = x[j;- ft, + 1]... x\j] при ft, < ft. Як же знайти значения ft,? Давайте перелічимо ті факти, які нам на даний час відомі:

  • фрагмент підрядка д:[1] ... x[k] збігається з його ж фраг­ментом x\j - ft]... x[j - 1];

  • x[k + 1] * x[j], тобто збігання наступних двох елементів тих фрагментів підрядка, які розглядаються, відсутнє.

Це означає, що якщо фрагмент підрядка х[1] ... x\j - 1] = = s[i - ft]... s[i - 2], x[j] * s[i - 1] i для цього випадку відоме зна­чения ft для наступного зсуву підрядка по рядку, то відповідно виконується і таке твердження: д:[1]... x[k] = s[i - ft]... s[i - 2], x[k + 1] * s[i - 1] (див. мал. 49). Який висновок з цього можна зробити? А висновок такий, що знову маемо справу зі старто­вого ситуацією і переходимо до початку наших міркувань, ви-значивши j - 1 = ft (ft < j - 1). А що було зроблено для того, щоб визначити, на яку кількість символів у x[lj... x[k] треба змісти-тися, щоб деякий його початок збігався з його кінцем? Оскіль-ки ft < j - 1, то ця інформація вже відома і є значениям елемен-та масиву /(ft).

Отже, «наступним кандидатом у кінці» рядка х[1] ... ... x{jl]x[j] є рядок х[1~\ ... x[f(k)]x[f(k) + 1], оскільки саме х[1] ... x[f(k)] є найдовшим кінцем х[1] ... x[k]. Якщо і він не придатний, то наступним є д:[1]... x[f{f(k)) + 1] тощо. Таким чи­ном, ми або знайдемо початок довжинир такий, що х[1]... х[р] є кінцем дг[1]... x\j], і тоді f(j) = p, або не знайдемо, і тоді f(j) = 0.

Описаний алгоритм, як бачимо, є рекурентним. Запишемо його у вигляді тексту Pascal-програми, де значения функції f будемо записувати у масив:

f[1]:=0; forj := 2ton do begin

k:=f[j-1];

while (x[j] <> x[k + 1]) and (k > 0) do k := f[k];

138

If (x[j]<>x[k+1])and(k = 0) thenffj] :=0 elsef[j]:=k+1; end;

Однак рекурентність досить часто можна замінити рекур-сією. Тому слід запропонувати й такий варіаит цього самого ал­горитму обчислення значень функції переходів для заданого підрядка х:

procedure КМР (k: word); begin if к = О

then f[j] := О elseifx[f[k-1] + 1] = x[j]

then fШ := f [k - 1] + 1 elseKMP(f[k]); end;

Побудова функції переходів здійснюється таким фрагмен­том алгоритму:

f[1]:=0;

forj := 2 torn do

KMP(j);

Побудова функції переходів за доиомогою рекурсивного ал­горитму може виглядати і так:

procedure КМР; var k, j: word; begin

f[1]:=0; forj := 2 to n do begin k:=f[j-1];

while (x[j] о x[k + 1]) and (k > 0) do к := f[k]; if (x[j]ox[k+ 1])and(k = 0) then f[j] := 0 elsef[j] :=k+ 1; end; end;

У цьому разі виклик процедури буде найпростішим: КМР;

Оцінимо загальну кількість порівнянь символів, виконува-них за цим алгоритмом. Позначимо через w(j) кількість вико-нань тіла циклу для відповідного значения ;' = 2, ..., т. Неваж-ко побачити, що кожне виконання тіла циклу while зменшує

139

значения k не менше як на 1. Звідси f[f\ <= f[j - 1] - w(j) + 1, тоб-то w(j) <- f[j - 1] - /[/] + 1. Тоді:

ш(2) + ш(3) + ... + ш(/п) <= Я1] - Я2] + 1 + Я2] - ЯЗ] + 1 + .« + + Я"і - 1] - Л>] + 1 = Я1] + т-1 <-j»-l.

Для кожного у відбувається порівнянь символів на 2 більше, ніж виконань тіла циклу: одне додаткове порівняння під час обчислення умови в заголовку циклу і одне - в умовному опера-торі. Звідси загальна кількість порівнянь символів не більша за 3(т - 1), тобто пропорційна т. Таким чином, загальну кіль-кість порівнянь символів під час побудови функції відступів можна оцінити як 0{т).

Тепер, нарешті, наведемо алгоритм пошуку входжень по­рядка х у рядок s. Причому будемо шукати всі можливі вхо­дження.

Нехай через t позначимо номер поточної позиції в рядку, j -номер поточної позиції у підрядку. їх початкові значения до-рівнюють 1.

Далі, поки t <- п, виконуємо наступні дії. Порівнюємо сим-воли x{j] і s[t]. Якщо вони рівні, то маємо входження х[1]... x[f\ у кінці рядка s[l]... s[t]. Якщо при цьому j = т., то можна пові-домити про входження підрядка х, почйнаючи з позиції tj + 1, i приступати до пошуків наступного його входження, поклавши / = f(m). Якщо ж у < т, то переходимо до наступної пари символів, збільшивши t i у на 1. Якщо s[t] i x{j\ нерівні при j > 1, то міняємо значения у на f[j — 1] + 1, а при у = 1 — збіль-шуємо t на 1. Проте зміни у" не мають сенсу, коли t = п. Ось i все.

Наведемо описаний алгоритм також мовою Pascal:

i := 1; j := 1;flag :=true; while (i <= n) and flag do begin

ifx[j] = s[i] then if j = m then begin write(f_out, 'Text has found at the position: #'); writeln(f_out, i - j + 1); flag := false; end else begin i:=i + 1; j:=j + 1 end else

140

if i <= n - m + 1 then begin ifj>1

theni:=i-f[j-1] else i := i + 1;

j:=1; end end; if flag then writeln(f_out, 'Text hasn"t found');

Оцінимо тепер кількість порівнянь символів під час вико-нання цього алгоритму. Нескладно помітити, що під час кож­ного виконання тіла циклу значения t збільшується на 1 або значения j зменшується прииаймні на 1 присвоюванням /[/'] чи f[j - 1] + 1. Позначимо через b(t) початкове значения / під час чергового значения t = 1, 2, ..., n, а через w(t) — кількість змен-шень j під час відповідного значения t. Оскільки під час збіль-шення t значения у або не змінюється, або збільшується на 1, то маемо, що b(t) <= b(t - 1) - w(t) + 1 при t> 1.

Звідси w(t) <- b(t - 1) - b(t) + 1. Тоді:

w(l) + w(2) + ... + w(n) <= 1 + fe[l] - b[2] + 1 + 6[2] - b[3] + 1 + + ... + b[n - I] - b[n] + 1 - n + b[l] - b[n] <- n + 1.

3 алгоритму очевидно, що порівнянь символів відбувається рівио стільки, скільки збільшень t і зменшеньу' разом. Оскільки t збільшується п - 1 разів, загальна кількість порівнянь сим-волів не більша за 2п, тобто пропорційна п, і оцінюється як 0(л).

3 урахуванням побудови функції переходів загальна кіль-кість порівнянь символів для будь-яких рядків довжини п і т оцінюеться як О(т) + О(л), або 0(п + т).

Оскільки описаний метод є не дуже простим щодо його усві-домлення, то дозволимо собі ще раз зробити остаточний висновок у вигляді таких основных постулатів методу КМП-пошуку:

  1. для пошуку наступного входження підрядка довжиною т у рядок довжиною п можна робити зсув лише на k позищй, де 1 <= k <- т;

  2. якщо знайдено кількість позицій зсуву k для підрядка, то цеозначав, щох[1] = х[п -k +1], х[2] = х[т -k + 2],..., x[k] = х\т\, тобто існує такий початок підрядка в h символів, що збігається з його кінцем довжиною також у k символів. Це означав, що можна визначити наперед, на скільки позицій треба зсунути підрядок у межах від 1 до т, якщо попередне його повне накла-дання не збігається;

  3. при накладанні з деякої позиції на рядок шуканого під-рядка незбігання можна визначити раніше, не переглядаючи

141

весь підрядок. Наприклад, незбігання відбулося на символі / заданого підрядка. Посилаючись на п. 1, можна зробити висно-вок, що тепер зсув можна робити лише в межах / символів по­рядка, де j < т. Посилаючись на п. 2, можна знайти таке ft., що визначає такий початок підрядка в ft. символів, що збігається з його кінцем.

/ Запитання для самоконтролю

1.Хто є авторами методу пошуку підрядка у рядку, який розгля-даеться?

  1. Чому метод КМП-пошуку можна віднести до методу, що вико-ристовує скінченний автомат?

  2. Поясніть поетапно ідею методу КМП-пошуку на конкретному прикладі, супроводжуючи свої пояснения мапюнками.

  1. Що являє собою функція переходів для методу КМП-пошуку?

  1. Який вміст елементів функції переходів для методу КМП-по­шуку?

  2. Наведіть рекурентний алгоритм побудови функції переходів для методу КМП-пошуку.

  3. Запишіть наведений у пункті 6 алгоритм у вигляді фрагмента тексту Pascal-програми.

  4. Наведіть рекурсивний алгоритм побудови функціі переходів для методу КМП-пошуку.

  5. Запишіть наведений у пункті 8 алгоритм у вигляді тексту Pascal-процедури.

  1. Запишіть алгоритм пошуку входження підрядка у рядок з вико-ристанням побудованоі функції переходів у вигляді фрагмента тексту Pascal-програми.

  2. Сформулюйте основні постулати методу КМП-пошуку.

  3. Якою є оцінка ефективності роботи методу КМП-пошуку? Обгрунтуйте цю оцінку.

Завдання

  1. Реалізувати алгоритм пошуку підрядка х у рядку s, засто-сувавши метод КМП-пошуку. Для побудови функції переходів використати рекурентний алгоритм.

  2. Реалізувати алгоритм пошуку підрядка х у рядку в, засто-сувавши метод КМП-пошуку. Для побудови функпд переходів використати рекурсивний алгоритм.

  3. Протестувати алгоритм, реалізований у завданні 1, для конкретного прикладу.

  4. Протестувати алгоритм, реалізований у завданні 2, для конкретного прикладу.

5- Порівняти результати виконання алгоритмів у завдан-нях 1, 2 стосовно кількості порівнянь символів підрядка і ряд­ка з алгоритмом лінійного пошуку.

6. Зробити письмовий аналіз виконання завдання 5.

142