Musala Soft Logo Конкурс по програмиране на Musala Soft и PC Magazine Bulgaria PC Magazine Bulgaria Logo
  Състезателна система
Сезон 2010 - 2011
Сезон 2009 - 2010
Сезон 2008 - 2009
Сезон 2007 - 2008
Сезон 2006 - 2007
Сезон 2005 - 2006
Сезон 2004 - 2005
Сезон 2003 - 2004
Сезон 2002 - 2003
Сезон 2001 - 2002
Правила
Задача 1
Задача 2
Задача 3
Задача 4
Задача 5
Задача 6
Класиране
Финален кръг
Сезон 2000 - 2001
Задача 6 - ОБИР НА БАНКА - Анализ


И шестата (последна) задача от задочната част на Конкурса не беше никак лесна (включително и за журито). Очевидно е, че задачата може да се раздели на две части - намиране на всички мв-палиндроми и избиране на средния от тях при лексикографска наредба. Да означим с S=S1 S2… SN зададения текст. Нека първо разгледаме един директен подход за решаване на задачата. Директното намиране на нечетните мв-палиндроми може да стане по следния начин. За всяка буква Si на зададения текст, без крайните две, опитваме да разширим тази буква до нечетен палиндром, т.е. за k=1,2,… проверяваме дали Si-k е равна на Si+k. При първото несъвпадение прекратяваме проверките, тъй като е намерен нечетен мв-палиндром (според условието една буква не образува палиндром). Аналогично (започвайки от всяка двойка съседни еднакви букви) намираме всички четни мв-палиндроми. За пестене на памет и удобство всеки намерен мв-палиндром можем да запомним с началото и края му в текста (или с началото и дължината му). Очевидно сложността на този алгоритъм в най-лошия случай е O(N 2) (например, ако текстът е съставен само от една и съща буква). За решението на втората част ще ни е необходимо да сравняваме мв-палиндроми. Директният подход би бил да сравняваме двата мв-палиндрома буква по буква. Това ни довежда до сложност O(N) в най-лошия случай на алгоритъма за сравняване на палиндромите. Нека намерените мв-палиндроми са M на брой. Директното намиране на средния би било да сортираме всички палиндроми с O(M.logM) сравнения, т.е при грубо сравняване за тази част ще получим алгоритъм със сложност O(N.M.logM). В най-лошия случай M ще бъде O(N), но големината му в общия случай зависи от конкретните входни данни. Разбира се сортирането е ненужно за намиране на среден елемент на множество. За намиране на К-тия пореден елемент на множество съществуват много по-добри алгоритми. Алгоритъми със сложност O(M) сравнения могат да се намерят във всяка сериозна книга по алгоритми (виж например Introduction to Algorithms, T. Cormen, Ch. Leiserson, R. Rivest). Алгоритъмът със сложност O(M) сравнения в най-лошия случай е труден за написване (и с голяма константа). За нашите цели е напълно достатъчен много по-лесния за реализация алгоритъм със същата сложност, но в средния случай (алгоритъмът е подобен на quick_sort). Така получаваме решение на втората част със сложност O(N.M) в най-лошия случай или окончателно за задачата O(N 2). За максималните стойности на параметрите на задачата това е недостатъчно. Сега ще се спрем на една сложна техника за решаване на задачи с низове, която може да се използва за много по-бързо решаване не само на първата, но и на втората част на задачата. Суфикс на низа S=S1 S2… SN е всеки подниз на S, завършващ с буквата SN. Да означим с S*=S1 S2SN$, където $ е знак, който не е измежду буквите на S. Суфиксно дърво Т на низа S е кореново дърво с N+1 листа, ребрата на което са надписани със поднизове на S*, така че:
  • първите букви на надписите на ребрата излизащи от един връх са различни;
  • конкатенациите на надписите по ребрата намиращи се на път от корена до листа L(S,i) е точно суфикса Si Si+1SN$

     Суфиксно дърво на S = 'abcabcabc'  Макар и трудно построяването на суфиксно дърво на зададен низ може да се направи със сложност O(N). На http://www.math.tau.ac.il/~haimk/seminar00/ могат да се намерят презентации свързани с построяването на суфиксно дърво,а на http://dogma.net/markn/articles/suffixt/suffixt.htm да се прочете една добра статия на същата тема. Най-лесен за имплементация е алгоритъмът на E. Ukkonen. За нашата задача ще е необходимо т.н. обобщено суфиксно дърво на S и S r -обратния на S. Това е суфиксно дърво за суфиксите едновременно на S и S r, като за да се различават суфиксите на S от тези на S r, се вземат различни завършващи знаци, например $ и #. За решението ще ни е необходимо и следното понятие: НОП или най-близък общ предшественик (LCA - Lowest Common Ancestor) на два върха в кореново дърво T наричаме връх, които лежи на всеки от пътищата от тези върхове до корена и е най-отдалечения от корена такъв връх. На фигурата, НОП за лист 2 и лист 5 е върха с конкатенация “bcabc” на надписите по ребрата, намиращи се на пътя от корена до него. Да означим с НОП(La,Lb) дължината на низа, който е конкатенация на надписите на ребрата по пътя от корена на суфиксно дърво до НОП на листата La и Lb. За разгледания пример НОП( L(S,2), L(S,5) ) = 5. НОП(La,Lb) може да се реализира със сложност O(1), като изисква предварителна обработка със сложност O(|VT|), където VT е множеството от върхове на суфиксното дърво, като|VT| = O(N). В решението на журито бе използван алгоритъмът на Schieber и Vishkin. Някои от участниците използваха алгоритъм за решаване на задачата, известна като RMQ (Range Minimum Query), която е пряко свързана с LCA. За подробности относно RMQ, виж http://www.scs.carleton.ca/~morin/teaching/tds/refs/bf-c00.pdf . За решението на първата част на задачата може да се използва следният алгоритъм:

    • 1. Построяваме обобщено суфиксно дърво T на S и Sr;
    • 2. Намираме нечетните мв-палиндроми по следния начин: за всяко K=2,3,…,N-1, пресмятаме d=НОП(L(S,k+1), L(S r,N-K+2)). Ако d>0 имаме нечетен палиндром с начална позиция K-d и крайна позиция K+d.
    • 3. Намираме четните мв-палиндроми по следния начин: за всяко K=1,2,…,N-1, пресмятаме d=НОП(L(S,k+1), L(S r,N-K+1)). Ако d>0 имаме четен палиндром с начална позиция K+1-d и крайна позиция K+d.

    За по-бързо решение на втората част на задачата ще използваме също НОП. Нека d = НОП(LX,LY), където LX е листът в Т, съответстващ на суфикса, на който X е префикс; аналогично за LY. Да дефинираме функцията Less(X,Y) с аргументи мв-палиндромите X и Y, с дължини dX и dY, която връща стойност истина, ако X е лексикографски преди Y и неистина - в противен случай:

    • Ако dX < d, тогава Less := dX < dY;
    • в противен случай ако dY < d, тогава Less := false;
    • в противен случай Less := DFS(LX) < DFS(LY).

    Като DFS(v) ни дава номера на v при preorder-обхождането на T в дълбочина, при което наследниците на текущия връх се обхождат в ред съответстващ на лексикографската наредба на изходящите ребра. Да отбележим само, че DFS номерирането е част от работата на алгоритъма на Schieber и Vishkin и не изисква допълнително време. Така сравняването на два мв-палиндрома става за константно време и втората част на задачата се реализира със сложност O(M).По този начин получаваме за цялата задача алгоритъм със сложност O(N+M)=О(N). Контстантите на този алгоритъм, разбира се, не са малки, но той може да се реализира за разумно време (3-4 дни) и е много ефективен. И все пак имаме участник, които без да се задълбочава в споменатите по-горе теории е успял да напише решение, което е много по-добро от решенията на останалите участници. Случайно или не, това е победителят от първия кръг от задочната част на Конкурса - Камен Добрев. Решението, според автора, използва техниката “Разделяй и владей” и описанието му може да се намери тук.


  • Анализи на решения изпратени от участници:

  • Камен Добрев (RTF Format)
  • Недко Маринов (RTF Format)

  • За въпроси можете да ни пишете на адрес: konkurs@musala.com.

    Supported by Musala Soft Ltd.

    Copyright 2000-2010 by Musala Soft Ltd. All rights reserved.