|
|
Linia 204: |
Linia 204: |
|
| |
|
| ''(Rozwiązanie opracował Jakub Radoszewski)'' | | ''(Rozwiązanie opracował Jakub Radoszewski)'' |
| | </div> |
| | </div> |
| | |
| | =='''Zadanie 9''' == |
| | |
| | Udowdnij że algorytm Oszczędny KMP wykonuje co najwyżej 3/2 n porównań |
| | <div class="mw-collapsible mw-made=collapsible mw-collapsed"> |
| | Rozwiązanie |
| | |
| | <div class="mw-collapsible-content" style="display:none"> |
| | |
| | By wykazać, że algorytm Oszczędny-MP wykonuje co najwyżej <math> \frac{3}{2}n</math> |
| | porównań, pogrupujemy te porównania w dwie szufladki: <math> A</math> i <math> B</math> . Pokażemy, że |
| | w szufladce <math> A</math> będzie co najwyżej <math> n</math> porównań, a w szufladce <math> B</math> co najwyżej |
| | <math> \frac{n}{2}</math> porównań. |
| | |
| | Do szufladki <math> A</math> wrzucamy: |
| | |
| | <br> |
| | Wszystkie udane porównania dokonane w trakcie szukania wzorca <math> x'</math> . |
| | |
| | <br> |
| | Wszystkie nieudane porównania pierwszej litery wzorca <math> x'</math> (czyli litery |
| | <math> b</math> ), dokonane w trakcie szukania wzorca <math> x'</math> . |
| | |
| | <br> Wszystkie porównania początkowych liter <math> a</math> , za wyjątkiem porównań |
| | na tych pozycjach, gdzie wcześniej szukaliśmy litery <math> b</math> --- pierwszej litery |
| | wzorca <math> x'</math> --- i nie znaleźliśmy jej. |
| | <br><br> |
| | |
| | Do szufladki <math> B</math> wrzucamy wszystkie pozostałe porównania, czyli: |
| | |
| | |
| | <br> |
| | Wszystkie nieudane porównania dokonane w trakcie szukania wzorca <math> x'</math> , |
| | za wyjątkiem nieudanych porównań pierwszej litery; są to nieudane porównania |
| | dokonywane w momencie, gdy znaleźliśmy już jakiś niepusty prefiks <math> x'</math> . |
| | |
| | |
| | <br> Porównania początkowych liter <math> a</math> na tych pozycjach, gdzie wcześniej |
| | szukaliśmy litery <math> b</math> i nie znaleźliśmy jej. |
| | <br><br> |
| | |
| | |
| | Zauważmy, że w algorytmie MP pozycje tekstu, na których nigdy nie było |
| | żadnego udanego porównania to dokładnie te pozycje, na których nie udało |
| | się znaleźć pierwszej litery wzorca (lub pozycja jest pod sam koniec tekstu |
| | i nie ma już szans na znalezienie wzorca, algorytm już się zakończył). |
| | Dodatkowo, zarówno algorytm MP jak i algorytm Oszczędny-MP ma taką właściwość, |
| | że jeśli na pewnej pozycji tekstu było udane porównanie, to ta pozycja |
| | tekstu już nigdy nie będzie porównywana --- o niej ,,wiemy już wszystko''. |
| | W związku z tym każde porównanie z szufladki <math> A</math> wykonuje się na innej |
| | literze tekstu, czyli tych porównań jest co najwyżej <math> n</math> . |
| | |
| | Spójrzmy teraz, jak zmienia się wskaźnik, na jakiej pozycji szukamy teraz |
| | wzorca <math> x</math> . Zauważmy, że prefikso-sufiks słowa <math> x[1\ldots s]</math> dla <math> s > k</math> |
| | jest długości co najwyżej <math> s - k - 1</math> --- litery <math> a</math> tego prefikso-sufiksu |
| | muszą się zaczynać za literą <math> b</math> na pozycji <math> k+1</math> . W związku z tym |
| | w momencie nieudanego porównania, które wystąpiło |
| | gdy znaleziony już został niepusty prefiks słowa <math> x'</math> , wskaźnik ,,gdzie |
| | teraz szukamy'' przesuwa się o conajmniej <math> k+1</math> . Tak też jest, |
| | gdy znajdziemy całe słowo <math> x'</math> --- przesuwamy się do prefikso-sufiksu |
| | słowa <math> x</math> . Dodatkowo, każde nieudane porównanie litery <math> b</math> z pozycji |
| | <math> k+1</math> w słowie <math> x</math> przesuwa wskaźnik o jeden. |
| | |
| | W związku z tym: |
| | |
| | |
| | <br> |
| | Porównania z szufladki <math> B</math> , podpunkt <math> 1</math> przesuwają wskaźnik o conajmniej |
| | <math> k+1 \geq 2</math> . |
| | |
| | |
| | <br> |
| | Po co najwyżej <math> k</math> porównaniach z szufladki <math> B</math> , podpunkt <math> 2</math> wskaźnik |
| | przesunie się o conajmniej <math> k+1</math> . Dodatkowo, każde takie porównanie oznacza, |
| | że wcześniej na tym miejscu było nieudane porównanie litery <math> b</math> . Wliczając |
| | przesunięcia pochodzące od tych porównań otrzymujemy, że |
| | wskaźnik przesunął się o conajmniej <math> k+1+L \geq 2L</math> , gdzie <math> L</math> to liczba |
| | takich porównań liter <math> a</math> w jednej próbie znalezienia wzorca <math> x</math> . |
| | <br><br> |
| | |
| | |
| | Czyli każde porównanie z szufladki <math> B</math> przesuwa wskaźnik ,,gdzie teraz |
| | szukamy'' o conajmniej <math> 2</math> , czyli tych porównań jest nie więcej niż |
| | <math> \frac{n}{2}</math> . |
| | |
| | |
| | |
| | ''(Rozwiązanie opracował Marcin Pilipczuk)'' |
| </div> | | </div> |
| </div> | | </div> |
Zadanie 1
Uzasadnij poprawność algorytmu obliczającego długość najkrótszego słowa pokrywającego dany tekst.
Rozwiązanie
Niech
będzie rozmiarem minimalnego pokrywającego słowa dla prefiksu . Poprawność wynika z następującego
faktu: \
Zadanie 2
Udowodnij, że w wersji on-line algorytmu KMP mamy
Rozwiązanie
Wystarczy wykazać, że
Fakt ten wynika z lematu o okresowości i z definicji tablicy P'.
Zadanie 3
Udowodnij, że w wersji on-line algorytmu KMP mamy
Rozwiązanie
Słowa Fibonacciego definiujemy następująco:
Na przykład:
Niech oznacza słowo Fibonacciego z obciętymi ostatnimi dwoma symbolami. Jeśli jako wzorzec weźmiemy słowo Fibonacciego , a jako tekst słowo to przy wczytywaniu -ego symbolu algorytm ma opóżnienie logarytmiczne, iterujemy razy operację: .
Zadanie 4
Udowodnij poprawność algorytmu na cykliczną równoważność słów.
Rozwiązanie
Zdefiniujmy:
oraz dla pewnego ,
oraz dla pewnego .
Skorzystamy z prostego faktu:
Jeśli lub , to nie są równoważne.
Poprawność algorytmu wynika teraz z tego, że po każdej głównej iteracji zachodzi niezmiennik:
\ oraz \ .
Zadanie 5
Dla jakich tekstów algorytm na cykliczną równoważność słów wykonuje maksymalną liczbę porównan symboli?
Rozwiązanie
Dla tekstów postaci o tej samej długości.
Zadanie 6
Mamy zbiór słów, każde długości dwa, obliczyć długość minimalnego tekstu który zawiera wszystkie słowa.
Rozwiązanie
Weźmy graf, którego węzłami są litery, a słowa wejściowe odpowiadają krawędziom.
Wystarczy znalezć minimalną liczbę krawędzi, które trzeba dołożyć do grafu, by miał on
ścieżkę Eulera.
Dygresja Zadanie to było na Olimpiadzie Informatycznej pod nazwą pierwotek abstrakcyjny.
Ciekawe jest to, że problem robi się NP-zupełny, gdy wszytkie słowa wejściowe mają długość 3.
Zadanie 7
Udowodnij następującą ciekawą własność kombinatoryczną okresowości w tekstach. Niech oznacza najmniejszy wspólny dzielnik p,q.
Lemat [Lemat o okresowości]
Jeśli x ma okresy p, q oraz , to jest również okresem x.
Rozwiązanie
Lemat ten wynika z poprawności algorytmu Euklidesa z odejmowaniem, który oblicza nwd(p,q). Zauważmy, że jeśli są okresami, to p-q też jest okresem.
Zadanie 8
Lemat o okresowości można wzmocnić, osłabiając założenia. Udowodnij następujący lemat.
Lemat [Silny lemat o okresowości]
Jeśli x ma okresy p, q oraz , to jest również okresem x.
Rozwiązanie
Indukcja ze względu na p+q.
Zadanie 9
Udowdnij poprawność algorytmu KMP realtime
Rozwiązanie
Problem jaki musimy rozwiązać to właściwość algorytmu, którą nazwiemy
opóżnieniem. Polega ona na tym, że w danym kroku algorytm może wciąż
jeszcze rozważać właściwy prefiks aktualnego słowa i nie dotrzeć w ogóle
do rozważenia bieżącej litery. Pokażemy jednak, że w momencie, kiedy nastąpi
wystąpienie wzorca, kolejka zostanie opróżniona, co wystarczy do
dowodu poprawności algorytmu.
Dowód przeprowadzimy nie wprost - załóżmy, że w tym kroku kolejka
się nie opróżni i że jest to pierwsze wystąpienie wzorca, którego algorytm nie
zdąża wyśledzić. Zacznijmy rozważanie działania algorytmu od ostatniego
miejsca wcześniejszego od bieżącego, w którym kolejka się opróżnia.
W tym momencie zachodzi (nawet jeżeli w owym kroku było wystąpienie
wzorca w tekście, to ten warunek i tak będzie po tym kroku spełniony)
oraz . Pokażemy, że odtąd
aż do miejsca wystąpienia wzorca zachodzić będzie niezmiennik
Faktycznie, zastanówmy się co się może wydarzyć w dowolnym, kolejnym kroku algorytmu,
a co może zmienić wartość zmiennej :
Może być dwukrotnie wywołana instrukcja .
Wówczas wzrasta o , wzrasta co najmniej o ,
czyli niezmiennik dalej zachodzi.
Moze być raz wywołana instrukcja , a raz (w jakiejkolwiek
kolejności). Wówczas się nie zmienia, pozostaje bez zmian
lub wzrasta, co nie zaburza niezmiennika.
Mogą nastąpić dwie instrukcje powiększające o .
Wówczas maleje o , także maleje o , zatem niezmiennik pozostaje zachowany.
Kolejka \textit{nie} może się opróżnić, gdyż zakładamy, że to nie nastąpi
przed aktualnie rozważanym wystąpieniem wzorca. Instrukcja również
nie może wystąpić, ponieważ oznaczałoby to wcześniejsze od rozważanego
niezauważone przez algorytm wystąpienie wzorca w tekście.
Zatem niezmiennik jest zachowany od ostatniego momentu opróżnienia kolejki
do momentu niezauważonego wystąpienia wzorca. W chwili przetworzenia literki,
która powoduje wystąpienie zachodzi , czyli na mocy pokazanego niezmiennika
, . To z kolei daje żądaną sprzeczność.
(Rozwiązanie opracował Jakub Radoszewski)
Zadanie 9
Udowdnij że algorytm Oszczędny KMP wykonuje co najwyżej 3/2 n porównań
Rozwiązanie
By wykazać, że algorytm Oszczędny-MP wykonuje co najwyżej
porównań, pogrupujemy te porównania w dwie szufladki: i . Pokażemy, że
w szufladce będzie co najwyżej porównań, a w szufladce co najwyżej
porównań.
Do szufladki wrzucamy:
Wszystkie udane porównania dokonane w trakcie szukania wzorca .
Wszystkie nieudane porównania pierwszej litery wzorca (czyli litery
), dokonane w trakcie szukania wzorca .
Wszystkie porównania początkowych liter , za wyjątkiem porównań
na tych pozycjach, gdzie wcześniej szukaliśmy litery --- pierwszej litery
wzorca --- i nie znaleźliśmy jej.
Do szufladki wrzucamy wszystkie pozostałe porównania, czyli:
Wszystkie nieudane porównania dokonane w trakcie szukania wzorca ,
za wyjątkiem nieudanych porównań pierwszej litery; są to nieudane porównania
dokonywane w momencie, gdy znaleźliśmy już jakiś niepusty prefiks .
Porównania początkowych liter na tych pozycjach, gdzie wcześniej
szukaliśmy litery i nie znaleźliśmy jej.
Zauważmy, że w algorytmie MP pozycje tekstu, na których nigdy nie było
żadnego udanego porównania to dokładnie te pozycje, na których nie udało
się znaleźć pierwszej litery wzorca (lub pozycja jest pod sam koniec tekstu
i nie ma już szans na znalezienie wzorca, algorytm już się zakończył).
Dodatkowo, zarówno algorytm MP jak i algorytm Oszczędny-MP ma taką właściwość,
że jeśli na pewnej pozycji tekstu było udane porównanie, to ta pozycja
tekstu już nigdy nie będzie porównywana --- o niej ,,wiemy już wszystko.
W związku z tym każde porównanie z szufladki wykonuje się na innej
literze tekstu, czyli tych porównań jest co najwyżej .
Spójrzmy teraz, jak zmienia się wskaźnik, na jakiej pozycji szukamy teraz
wzorca . Zauważmy, że prefikso-sufiks słowa dla
jest długości co najwyżej --- litery tego prefikso-sufiksu
muszą się zaczynać za literą na pozycji . W związku z tym
w momencie nieudanego porównania, które wystąpiło
gdy znaleziony już został niepusty prefiks słowa , wskaźnik ,,gdzie
teraz szukamy przesuwa się o conajmniej . Tak też jest,
gdy znajdziemy całe słowo --- przesuwamy się do prefikso-sufiksu
słowa . Dodatkowo, każde nieudane porównanie litery z pozycji
w słowie przesuwa wskaźnik o jeden.
W związku z tym:
Porównania z szufladki , podpunkt przesuwają wskaźnik o conajmniej
.
Po co najwyżej porównaniach z szufladki , podpunkt wskaźnik
przesunie się o conajmniej . Dodatkowo, każde takie porównanie oznacza,
że wcześniej na tym miejscu było nieudane porównanie litery . Wliczając
przesunięcia pochodzące od tych porównań otrzymujemy, że
wskaźnik przesunął się o conajmniej , gdzie to liczba
takich porównań liter w jednej próbie znalezienia wzorca .
Czyli każde porównanie z szufladki przesuwa wskaźnik ,,gdzie teraz
szukamy o conajmniej , czyli tych porównań jest nie więcej niż
.
(Rozwiązanie opracował Marcin Pilipczuk)