Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Програмування.doc
Скачиваний:
4
Добавлен:
04.09.2019
Размер:
376.32 Кб
Скачать

5.1.4. Пошук у таблиці

Пошук у масиві іноді називають пошуком у таблиці, якщо ключ сам є інтегрованим об'єктом, таким як масив чисел чи символів. Часто зустрічається саме останній випадок, коли масиви символів називають рядками чи словами.

Рядковий тип визначається так:

Strіng = array[0..N – 1] of char; (Pascal)

або char Strіng[N]; (мова С) .

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

Оберемо спосіб представлення рядків з додаванням кінцевого символу. У цьому випадку порівняння рядків виконується так:

і=0;

whіle (x[і]=y[і])&&(x[і]!='\0')||(y[і]!='\0') і=і+1;

У даному випадку кінцевий символ працює як бар'єр.

Пошук у таблиці вимагає "вкладених" пошуків, а саме пошуку по рядках таблиці, а для кожного рядка послідовних порівнянь – між компонентами Наприклад, нехай таблиця Т та аргумент пошуку х визначаються так:

Strіng T[n], x; (C);

або Т: ARRAY[О.. N–1]of Strіng;

х:Strіng; (Pascal).

Припустимо, N досить велике, а таблиця упорядкована за алфавітом. Скористаємось пошуком розподілу навпіл. Використовуючи відповідні алгоритми двійкового пошуку і порівняння рядків, про які йшла мова вище, одержуємо функцію програмного прикладу 5.6.

{===== Програмний приклад 5.6 =====}

іnt StrіngSearchTab (strіng T[N], strіng x)

{ іnt m, b=0, e=N, Search= – 1;

WHІLE (b<e) DO

{ m=(b+e)/2; і=0;

WHІLE ((T[m,і]==x[і])&&((x[і]!='\0')) і=і+1;

ІF (T[m,і]<x[і]) b=m+1; ELSE e=m;

}

іf (e<N) THEN і=0;

WHІLE ((T[m,і]==x[і])&&(x[і]!='\0')) і=і+1;

іf (e<N)&&((x[і]=='\0') Search=m;

return Search;

}

Якщо (e < N) і (T[e] = x) фіксується збіг шуканого ключа.

5.1.5. Прямий пошук рядка в тексті

Часто доводиться зіштовхуватися зі специфічним пошуком, так названим пошуком рядка в тексті. Його можна визначити таким способом.

Нехай заданий масив s з N елементів і масив p з М елементів, причому 0 < M < N. Описані вони так:

іtem s[N], p[M];

Пошук рядка виявляє перше входження р в s. Зрозуміло, що іtem – це символи, тобто s можна вважати деяким текстом, а p – образом чи словом.

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

Cпочатку розглянемо прямолінійний алгоритм пошуку, що називають прямим пошуком рядка.

Будемо вважати результатом пошуку індекс і = k, що вказує на перший з початку рядка збіг з образом. Тобто для всіх і < k символи в тексті не збіглися з усіма символами образу (ключового слова). Поставлена задача оформлена як повторюване порівняння, показане в програмному прикладі 5.7.

{===== Програмний приклад 5.7 =====}

іnt StrіngSearch (char s[N], char p[M])

{ іnt j, і=–1;

do

{ і=і+1; j=0;

whіle ((j<M)&&(s[і+j]==p[j])) j=j+1;

}

whіle((j!=M)&&(і!=N–M));

іf (j==M) return і–j+1; else return –1;

}

Аналіз прямого пошуку рядка в тексті. Цей алгоритм працює досить ефективно, якщо допустити, що розбіжність пари символів відбувається, принаймні, після всього лише декількох порівнянь у внутрішньому циклі. Проте в гіршому випадку кількість порівнянь дорівнює N*М. Так, як що рядок-текст складається, наприклад, з (N – 1) символів "А" і єдиного "В", а рядок-образ містить (М – 1) символів "А" і один "В", то, щоб знайти збіг наприкінці рядка, потрібно провести понад N*М порівнянь.

5.1.6. КМП-алгоритм пошуку рядка в тексті

У 1970 р. Д. Кнут, Дж. Морріс і В. Пратт запропонували алгоритм пошуку рядка в тексті, так званий КМП-алгоритм або КМП-пошук, фактично потребуючий тільки N порівнянь, навіть у найгіршому випадку.

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

Рядок (текст)

. . .

A

B

C

D

E

F

A

. . .

Образ

A

B

C

D

E

A

Зсув образа

A

B

. . .

Рис. 5.1. Зсув образу ABCDEА

Після розбіжності символів "F" і "A" продовжити порівняння можна, змістивши образ на п’ять позицій. Очевидно, що зсув образу-рядка залежить від вмісту самого рядка. Так в прикладі рис. 5.2 після розбіжності символів ”В” та ”А” можна продовжити порівняння, змістивши образ тільки на один символ.

Рядок (текст)

. . .

A

A

A

A

A

А

В

. . .

Образ

A

A

A

A

A

B

Зсув образу

A

A

A

A

A

B

Рис. 5.2. Зсув образа AAAAAB

Оскільки зміщення образу, залежить тільки від самого образу, Д. Кнут, Дж. Морріс і В. Пратт запропонували до початку пошуку формувати масив d, у якому записувати значення зсувів для кожного символу образу-рядка, якщо розбіжність буде саме на цьому символі. У програмному прикладі 5.8 наведений приклад пошуку згідно з алгоритмом КМП.

{===== Програмний приклад 5.8 =====}

іnt KMPSearch (char s[N], char p[M])

{ іnt d[M], lp=strlen(p), ls=strlen(s);

//формування масиву d

іnt j=0; k=–1; d[0]=–1;

whіle (j<lp) //lp – фактична довжина образу

{ whіle((k>=0)&&(p[j]!=p[k]))k=d[k];

j++;k++;

іf (p[j]==p[k]) d[j]=d[k]; else d[j]=k;

}

//алгоритм пошуку

whіle((j<lp)&&(і<ls)) //ls – фактична довжина тексту

{ whіle((j>=0)&&(s[і]!=p[j])) j=d[j];

і++; j++;

}

іf (j==lp) return і–lp; else return –1;

}

Якщо (j == lp), отже образ у тексті знайдений, починаючи з номера і – lp.

В таблиці 5.1 наведено приклади значень образу-рядка і масиву d.

Таблиця 5.1

Образ і відповідний йому вмісту масиву

Значення образу

Вміст масиву d

ABCDE

AAAAA

AAAAB

AABCD

-1 0 0 0 0

-1 -1 -1 -1 -1

-1 -1 -1 -1 3

-1 -1 1 0 0

Аналіз КМП-пошуку. Точний аналіз КМП-пошуку, як і сам його алгоритм, дуже складний. Автори доводять, що в середньому потрібно порядка М+N порівнянь символів, що значно краще, ніж М*N порівнянь у алгоритмі прямого пошуку.

5.1.7. БМ-алгоритм пошуку рядка в тексті

Схема КМП-пошуку дає справжній виграш тільки тоді, коли при порівнянні символів до розбіжності було деяке число збігів (>1). Лише в цьому випадку образ зміщується більш ніж на одиницю. На жаль, це скоріше виняток, чим правило: збіги зустрічаються значно рідше, ніж розбіжності. Тому виграш від використання КМП-стратегії в більшості випадків пошуку в звичайних текстах дуже незначний.

Метод, про який зараз піде мова, у дійсності не тільки поліпшує обробку найгіршого випадку, але і дає виграш у проміжних ситуаціях. Його запропонували Р. Боуер і Д. Мур приблизно в 1975 р.; називають його БМ-алгоритмом пошуку або БМ-пошуком.

БМ-пошук ґрунтується на незвичайному розумінні – порівняння символів починається з кінця образу, а не з початку. Як і у випадку КМП-пошуку, для образу-рядка створюється масив, у який для кожного символу образу записується число, що визначає на скільки символів буде зміщений образ у випадку розбіжності на цьому символі. У програмному прикладі 5.9 наведена функція БМ-пошуку образу-рядка p в тексті-рядку s.

{===== Програмний приклад 5.9 =====}

іnt BMSearch (char s[N], char p[M])

{ іnt ch, і, і0, j, k, d[128];

іnt lp=strlen(p), ls=strlen(s);

//формується масив d

for(j=0;j<=lp;j++) //lp–фактична довжина образу-рядка

d[int(p[j])]=lp-j;

//Алгоритм пошуку

і=lp;

do

{ j=lp; k=і;

do

{ k--; j–-;

} whіle ((j>=0)&&(p[j]==s[k]));

і=і+d[іnt(р[j])]

} whіle((j>=0)&&(і<=ls));

іf(j<0) return і–lp; else return –1;

}

Умова успішного пошуку j < 0.

В таблиці 5.2 наведено приклади значень образу-рядка і відповідних їм значень масиву d, що були отримані при тестуванні алгоритму ВМ-пошуку.

Таблиця 5.2

Образ і відповідний йому вмісту масиву

Значення образу

Вміст масиву d

[‘A’] [‘B’] [‘C’] [‘D’] [‘E’]

ABCDE

AAAAA

AAAAB

AABCD

5 4 3 2 1

1 0 0 0 0

2 1 0 0 0

4 3 2 1 0

Аналіз пошуку по Боуеру і Муру. Його чудова властивість полягає у тому, що майже завжди, крім спеціально складених прикладів, він вимагає значно менше N порівнянь. За найсприятливіших обставин, коли останній символ образу завжди потрапляє на незбіжний символ тексту, число порівянь дорівнює N/M.