Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмдеу жане багдарламалау негиздери 4 г.doc
Скачиваний:
3
Добавлен:
01.07.2025
Размер:
2.73 Mб
Скачать

22 Дәріс тақырыбы: Қатарда іздеу. Кнут - Моррис - Пратт алгоритмі.

Кнут-Моррис-Пратт Алгоритм (КМП) кіруге

X=x[1]x[2]... x[n]

сөзін алады және l[1]... l[n] натурал сандар жиымын толтыра отырып, оны солдан оңға қарай әріптен кейін әріпті қарайды, мұнда

l[i]=сөздің ұзындығы l(x[1]...х[i])

(1 функциясы алдыңғы тармақта анықталды). Сөздермен: l[i] x[1]...x[i] сөзінің ең үлкен басының ұзындығы және сонымен бір мезгілде оның соңы болып табылады.

Мұның бәрінің шағын сөзді іздеуге қандай қатысы бар?

Басқаша айтқанда, А сөзі В сөзінің шағын сөзі болып табылатынын анықтау үшін КМП алгоритмін қалай пайдалану қажет?

Шешім. КМП алгоритмін A#B сөзіне қолданамыз, мұнда # - А-да да В-да да кездеспейтін арнайы әріп. А сөзі жиымда сандар арасында 1 А сөзінің ұзындығына тең сан болғанда ғана В сөзінің сөз асты болып табылады.

l[1]...l[i] кестесін толтыру алгоритмін суреттеу.

Шешім. l[1]...l[i] мәнінің бірінші і мән табылды деп аламыз. Біз сөздің кезекті әрпін оқимыз (яғни x[i+1]) және l[i+1] есептеуіміз қажет.

Басқаша айтқанда, бізді Z сөзінің басы және сонымен бір мезгілде оның соңы болатын

x[1]...x[i+1,

қызықтырады, одан біз ең ұзынын алуымыз қажет. Басы қайдан алынады? Олардың әрқайсысы (босын есептемегенде) x[i+1] әрпін көшіріп жазу арқылы Z' кейбір сөзінен алынады. Z' сөзі x[1]...x[i] сөзінің басы мен аяғы болып табылады. Алайда, x[1]...x[i] сөзінің басы мен аяғы болып табылатын кез келген сөз жарамды болмайды, олардан кейін x[i+1] әрпінің болуы тиіс.

Z сөзін іздеудің осындай рецептін аламыз. x[1]...x[i] сөзінің соңы болып табылатын барлық басын іздейміз. Олардың арасынан соңынан x[i+1] әрпі келетін лайықтысын таңдаймыз. Лайықтылардың арасынан ең ұзынын таңдап аламыз. Оның соңына х[i+1] қосып жаза отырып, ізделіп отырған Z сөзін аламыз. Енді біз әзірлеген дайындамаларды пайдалану және осы сөздің бірмезгілде басы мен соңы болып табылатын барлық сөздерді алдыңғы бөлімнен оған 1 функцияны қайталап қолдана отырып алуға болатынын еске түсіру қажет.

Алынатыны:

i:=1; 1[1]:=0;

{ l[1]..l[i] кестесі дұрыс толтырылған}

while i <> n do begin

len:= l[i]

{len - x[1]..x[i] сөзінің оның соңы болып табылатын басының ұзындығы; барлық ең ұзын басы қолайсыз болып шықты}

while (x[len+1]<>х[i+1]) and (len>0) do begin

{басы сәйкес келмейді, оған 1 функцияны қолданамыз}

len:=l[len];

end;

{сәйкес келетінін таптық немесе жоқ екеніне көз жеткіздік}

if x[len+1]=x[i+1] do begin

{х[1]..x[len] – ең ұзын сәйкес клеетін басы}

l[i+1]:=len+1;

end else begin

{сәйкес келетіні жоқ}

l[i+1]:= 0;

end;

i:=i+1;

end;

Алгоритмде қазір ғана келтірілген амалдардың саны С кейбір константы үшін Cn басым болмайтынын дәлелдеу.

Шешім. Бұл толық айқын емес: әрбір кезекті әріпті өңдегеннен кейін ішкі циклда көптеген итерацияларды қажет етуі мүмкін. Алайда әрбір мұндай итерация len-ді кем дегенде 1-ге азайтады, және бұл жағдайда l[i+1] l[i]-нен едәуір кем болады. Екінші жағынан, і-ні бір бірлікке көбейткен кезде l[i] 1-ден артық емеске өсуі мүмкін, сондықтан ол жиі және бірден кеміте алмайды - әтпесе кемітудің орны өсумен толтырылмайды.

l[i+1]<l [i] - (i-қадамдағы итерациялар саны)+1

теңсіздігін барынша дәл жазуға болады

немесе

(i-қадамдағы итерациялар саны)<= l[i]-l[i+1]+1

Осы теңсіздікті барлық і бойына қосу және итерациялардың ортақ саны үшін жоғарыдан баға алу қалады.

Осы алгоритмді п ұзындықтағы Х сөзі m ұзындықтағы Ү сөзінің сөз асты болып табылатынын анықтау үшін пайдаланатын боламыз. (жоғарыда суреттелген # арнайы бөліктегішінің көмегімен мұны қалай жасауға болады). Бұл ретте амалдар саны және қолданылатын жады да C(n+m}-нен артық болмайды. Cn артық емес жадымен қалай айналып өтуге болады (егер ізделіп отырған үлгі қысқа, ал оны іздеп отырған сөз ұзын болса не едәуір кем болуы мүмкін).

Шешім. А#В сөзіне КМП алгоритмін қолданамыз. Бұл ретте: l[1],...,l [n] есептелген мәндерін п ұзындықты Х сөзі үшін шығарамыз. Одан әрі біз қазіргі і үшін тек қана l[i] мәнін есте қалдырамыз – одан басқа және l[1]...l[n] кестесінен басқа бізге есептеу үшін ештеңе керек емес.

Практикада Х және Ү сөздері қатар орналаспауы мүмкін, сондықтан Х сөзін және содан кейін Ү сөзін қарауды түрлі циклдер түрінде ресімдеген ыңғайлы. Бұл сондай-ақ бөліктегішпен болатын қиындықтан құтқарады.

Сәйкес алгорит жазу (тексеруші, X=x[1]...x[n] сөзі Y=y[1]...y[m] сөзінің кіші сөзі бола ма]

Шешім. Алдымен l[1]...l[n] кестесін бұрынғыша есептейміз. Содан кейін мынадай программаны жазамыз:

j:=0; len:=0;

{len – Х сөзінің ең жоғарғы басының ұзындығы сонымен қатар y[1]..j[j] сөзінің соңы болып табылатын ұзындығы}

while (len<>n) and (j<>m) do begin

while (x[len+1]<>у[j+1]) and (len>0) do begin

{басы сәйкес келмейді, оған 1 функцияны қолданамыз}

len: = l[len];

end;

{сәйкес келетінін таптық немесе жоқ екеніне көз жеткіздік}

if x[len+1]=y[j+1] do begin

{x[1]..x[len] – ең ұзын сәйкес келетін басы}

len:=len+1;

end else begin

{сәйкес келетіні жоқ}

len:=0;

end;

j:=j+1;

end;

{егер len=n, X сөзі кездеседіь; әйтпесе бізХ-ты кездестіре алмай Ү сөзінің соңына жеттік}