Az utótagfa olyan fa , amely valamilyen karakterlánc összes utótagját tartalmazza (és csak azokat). Lehetővé teszi, hogy megtudja, hogy a w karakterlánc szerepel -e az eredeti t karakterláncban O (|w|) időben , ahol |w| a w karakterlánc hossza .
|
Lemma. A w helye akkor és csak akkor egyértelmű egy kompakt utótagfában, ha w a t nem beágyazott utótagja , vagy w jobboldali ág.
Bizonyíték. . Ha explicit, akkor lehet levél, vagy ágcsúcs, vagy gyökér (ebben az esetben a w is a t beágyazott utótagja ).
Ha levél, akkor t utótag is . Tehát nem lehet beágyazott utótag, mert különben valahol máshol jelenne meg a t karakterláncban : v a t utótagja úgy, hogy w a v előtagja . Ez a csomópont nem lehet levél.
Ha egy elágazó csomópont, akkor legalább két kimenő élnek kell lennie különböző címkékkel. Ez azt jelenti, hogy két különböző u , v utótag létezik , hogy w az u előtagja , w pedig a v előtagja , ahol v = wxs , u = , x . Ezért w jobb ág.
. Ha w a t nem beágyazott utótagja , akkor levélnek kell lennie. Ha w jobboldali ág, akkor két u és v utótag van , u = wxs , v = , x , akkor w egy elágazó csomópont. A lemma bebizonyosodott .
Most már könnyen belátható, hogy miért található meg a válasz arra a kérdésre, hogy a w szó benne van- e a t karakterláncban O(|w|) időben : csak azt kell ellenőrizni, hogy w hely (explicit vagy implicit) a cst( t ).
Az élcímkéknek a karakterláncban lévő pozícióra kell mutatniuk, hogy az utótagfa O(n) memóriát használjon . Az élcímke (p, q) részkarakterláncot vagy üres karakterláncot jelent, ha p > q.
Ukkonen bevezeti a nyitott élek elnevezést a levelekre végződő élekre. A nyitott élcímkék (p, ) helyett (p, |t|) , ahol a hossz mindig nagyobb, mint |t| .
Legyen T egy -fa. Legyen egy T csomópont , v a leghosszabb w utótag , amely egyben T csomópont is . A tól - ig címkézetlen élt utótag hivatkozásnak nevezzük . Ha v = w , akkor atomnak nevezzük .
Nyilatkozat. Az ast( t ) és a cst( t$ ), ahol $ t , minden utótag hivatkozás atomi.
Bizonyíték. A $ szimbólumot őr szimbólumnak nevezik . Az első rész (az ast( t )-hoz) a definícióból következik, mivel a helyek explicitek. A második rész bizonyításához (cst( t ) eset) meg kell mutatnunk, hogy minden csomóponthoz tartozik cst( t ) csomópont is. Ha egy csomópont cst( t ), akkor vagy levél, vagy elágazó csomópont. Ha egy levél, akkor az aw nem a t beágyazott utótagja . A védőszimbólumnak köszönhetően a lemmából az következik, hogy minden utótag (beleértve a gyökeret, az üres utótagot is) explicit, mivel csak a gyökér beágyazott utótag. Ezért w egy levél vagy egy gyökér. Ha egy elágazó csomópont, akkor aw jobb oldali elágazás, akárcsak w . Ezért a helyet a lemma határozza meg. Az állítás bebizonyosodott.
Amint ebből a bizonyításból következik, az őr szimbólum minden utótagnál garantálja a levelek meglétét. Egy ilyen karakternél nem lehetnek beágyazott utótagok az üresen kívül. Ha kihagyjuk az őr karaktert, akkor egyes utótagok beágyazódhatnak, és helyük implicitté válik.
Nyilatkozat. Egy kompakt utótag fa olyan formában ábrázolható, amely O(n) memóriát igényel.
Bizonyíték. Az utótag fa utótagonként legfeljebb egy levelet tartalmaz (pontosan egy gyámjellegű). Minden belső csomópontnak elágazó csomópontnak kell lennie, tehát egy belső csomópontnak legalább két gyermeke van. Minden ág legalább eggyel növeli a levelek számát, így legfeljebb n belső csomópontunk van, és legfeljebb n levelünk van .
Az élcímkék karakterláncainak megjelenítéséhez indexelést használunk a forráskarakterláncon, a fent leírtak szerint. Minden csomópontnak legfeljebb egy szülője van, így az élek teljes száma nem haladja meg a 2n -t .
Hasonlóképpen minden csomópontnak legfeljebb egy utótag hivatkozása van, ekkor az utótag hivatkozások száma szintén 2n -re korlátozódik . Az állítás bebizonyosodott.
Példaként egy 2n-1 csomópontot tartalmazó utótagfára tekintse meg a szó fáját . A t karakterlánc atomi utótagjának fa mérete O( ) .
Az mcc algoritmus egy üres fával indul, és a leghosszabbtól kezdve utótagokat ad hozzá. Az mcc algoritmus nem on-line algoritmus, vagyis a teljes sorra szükség van a működéséhez. A helyes működés megköveteli, hogy a karakterláncot a többitől eltérő speciális karakter fejezze be, így egyetlen utótag sem lehet másik utótag előtagja. A fa minden utótagja egy levélnek felel meg. Az algoritmushoz meghatározzuk - az aktuális utótagot ( lépésben ), ( fej ) - az utótag legnagyobb előtagját , amely egyben egy másik utótag előtagja is , ahol . ( farok ) definíció szerint .
Az mcc algoritmus kulcsgondolata a és a közötti kapcsolat .
Lemma. Ha hol van az ábécé egy betűje, egy karakterlánc (lehet üres), akkor egy előtag .
Bizonyíték. Hadd . Ekkor létezik olyan , , amely egyszerre előtagja és előtagja is . Akkor egy előtag , és ezért a fej előtagja . A lemma bevált.
Ismerjük a helyét , és ha van utótag-hivatkozásunk, gyorsan a fejelőtag helyére ugorhatunk anélkül , hogy meg kellene találnunk az utat a fa gyökerétől. Előfordulhat azonban, hogy a hely nem egyértelmű (ha a hely nem volt explicit az előző lépésben), és előfordulhat, hogy az utótag hivatkozás még nincs beállítva a csomóponthoz . McCray megoldása két lépésben talál egy csomópontot : „újraszkennelés” és „letapogatás”. A csomóponttól felfelé haladunk a fán, amíg nem találunk egy utótag hivatkozást, követjük azt, majd újra beolvassuk a hely elérési útját (ami egyszerű, mert tudjuk, hogy a hossza és a hely létezik, így nem kell a teljes élt olvasnunk a fán lefelé mozgó címkék esetén csak a kezdőbetűket és a szavak hosszát tudjuk ellenőrizni).
Az ábra ezt az elképzelést mutatja be. Ahelyett, hogy megpróbálna megtalálni a gyökértől a csomópontig vezető útvonalat , az algoritmus a következőre ugrik , követi az utótagot , újra beolvassa a (esetleg implicit) hely elérési útját, és karakterről karakterre haladva meg kell keresni az elérési utat .
Az algoritmus három részből áll.
1. Először is meghatározza az előző fej szerkezetét, megkeresi a következő elérhető utótag hivatkozást, és követi azt.
2. Ezután újra beolvassa az előző fej azon részét, amelynek hossza ismert (ez a rész neve ).
3. Végül az algoritmus beállítja az utótag hivatkozását , megvizsgálja a többit (névvel ), és új levelet ad hozzá .
Ha nincs hely, az újrakeresés második fázisában létrejön egy elágazó csomópont . Ebben az esetben a vizsgálat nem szükséges, mert ha hosszabb lenne, mint , akkor jobb oldali ág lenne, de a lemma szerint ez is jobb ág, tehát a csomópontnak már léteznie kell. A csomópont a harmadik fázisban jön létre, ha a hely még nem egyértelmű.
1. algoritmus (mcc, McCreight) Bemenet: string t 1: T : = üres fa; 2: fej 0 := ; 3: n := hossz(t) ; 4: for i := 1-től n -ig csináld 5: keress , , olyat, hogy a. fej i-1 = , b. ha az i-1 csomópont őse nem a gyökér ( gyökér ) , jelölje , ellenkező esetben c. és ( | fej i-1 |=0) 6: ha ( >0) , akkor 7: kövesd az utótag hivatkozást a csomóponttól a -ig ; 8: end if 9: := Rescan( ) ; 10: utótag hivatkozás beállítása től 11-ig: ( , tail i ) := Vizsgálat ( , suf i - );12: a farok i -nek megfelelő levél hozzáadása ; 13: vége forVegye figyelembe, hogy ha akkor és ugyanolyan gyorsan felismerhető, mint az utótag hivatkozást követve az algoritmus 7. sora szerint.
Az Újrakeresési eljárás helyet keres . Ha a hely még nem egyértelmű, egy új csomópont kerül hozzáadásra. Ez az eset akkor fordul elő, ha a fejet ( ) teljes egészében nézzük: ha a fej hosszabb (és a csomópont már definiált), akkor kettőnél több utótagból kell állnia, és egyben a bal oldali ága is . A hely csak akkor lehet explicit, ha az adott csomópont már elágazó csomópont, és ha nem volt bal oldali ág , akkor hosszabbnak kell lennie, mert hosszabb előtagot találtak.
A Scan eljárás megkeresi a fa mélységét, és visszaadja a pozíciót.
1. eljárás Rescan(n, ) Bemenet : csomópont n , 1. sor: i :=1; 2: míg én | | do 3: keresse meg az e=n n' élt , w 1 = 1 ; 4: ha i +| w |>| |+1 majd 5: k :=| -i +1 ;6: e él felosztása új m csomóponttal és n m és m n' élekkel ; 7: return m ; 8: vége, ha 9: i := i +| w |; 10: n := n' ; 11: end while 12: return n' ; 2. eljárás Scan(n, ) Bemenet : csomópont n , 1. sor: i :=1; 2: míg van él e = n n' , w 1 = i do 3: k :=1; 4: míg w k = i és k | w | do 5: k := k +1; 6: i := i +1; 7: vége, míg 8: ha k >| w | akkor 9: n := n' ; 10: else 11: e él felosztása új m csomóponttal és n m és m n' élekkel ; 12: return ( m , i ,..., ); 13: end if 14: end while 15: return ( n , i ,..., );Az az algoritmus, amelyet Esko Ukkonen egy utótagfa lineáris időben történő felépítésére talált ki, valószínűleg a legegyszerűbb ezen algoritmusok közül. Az egyszerűség abból adódik, hogy az algoritmus kezdetben egy egyszerű, de nem hatékony módszernek tekinthető, amely néhány "józan ész" megvalósításával a legrosszabb körülmények között is eléri a legjobb algoritmusok szintjét a futási idő tekintetében. Ugyanez történik a PDF-ben ábrákkal .
Az algoritmus és a megvalósítás részletes leírása C++ nyelven : cs.mipt.ru (oroszul) és marknelson.us (angolul)
Az Ukkonen algoritmushoz szükségünk van
1) Implicit utótag fák 2) Az algoritmus általános leírása 3) Algoritmus optimalizálásUkkonen algoritmusa implicit utótagfák sorozatát építi fel, amelyek közül az utolsót valódi S karakterlánc utótagfává alakítja .
Неявное суффиксное дерево строки S — это дерево, полученное из суффиксного дерева S$ удалением всех вхождений терминального символа $ из меток дуг дерева, удалением после этого дуг без меток и удалением затем вершин, имеющих меньше двух детей. Неявное суффиксное дерево префикса S[l..i] строки S аналогично получается из суффиксного дерева для S[l..i]$ удалением символов $, дуг и вершин, как описано выше.
Bármely S karakterlánc implicit utótagfájának kevesebb levele lesz, mint az S$ karakterlánc utótagfájának, akkor és csak akkor, ha az S utótagjai közül legalább az egyik egy másik utótag előtagja. A $ terminált hozzáadtuk az S végéhez, hogy elkerüljük ezt a helyzetet. A valódi utótagfa meghatározásakor ez nagyon fontos szempont. Ha azonban az S olyan karakterrel végződik, amely sehol máshol nem szerepel az S-ben, akkor az S implicit utótagfában minden utótaghoz tartozik egy levél, és ezért valódi utótag fa.
Bár az implicit utótagfának nem minden utótaghoz van levele, az összes S utótagot kódolja – mindegyiket az implicit utótagfa gyökeréből származó útvonal karakterei ejtik ki. Ha azonban ez az út nem végződik levéllel, akkor nem lesz jelző, amely az út végét jelzi. Így maguk az implicit utótagfák valamivel kevésbé informatívak, mint a valódiak. Csak Ukkonen algoritmusának segédeszközeként használjuk őket, hogy valódi utótagfát kapjunk S-hez.
Ukkonen algoritmusa egy implicit T i utótagfát épít az S karakterlánc minden S[l..i] előtagjához, T 1 -től kezdve, és i-t eggyel növeli, amíg T m fel nem épül . Az S valódi utótag fája T m -ből származik , és az egész feladat O(m) időt vesz igénybe. Ukkonen algoritmusát úgy fogjuk elmagyarázni, hogy először bemutatunk egy módszert, amellyel minden fa O(m³) idő alatt épül fel, majd optimalizáljuk ennek a módszernek a megvalósítását úgy, hogy a deklarált sebességet elérjük.
Ahhoz, hogy ezt az általános leírást algoritmussá alakítsuk, pontosan meg kell határoznunk az utótag folytatását. Legyen S[j..i] = β az S[1..i] utótagja. A j folytatásban, amikor az algoritmus megtalálja a β végét az aktuális fában, folytatja a β-t annak biztosítására, hogy a βS(i + 1) utótag jelen legyen a fában. Az algoritmus a következő három szabály egyike szerint működik.
1. szabály. Az aktuális fában a β útvonal egy levélben végződik. Ez azt jelenti, hogy az út a β-vel jelölt gyökértől valamilyen "levél" ív (a levélben lévő ív) végéig tart. A fa megváltoztatásakor adja hozzá az S(i + 1) szimbólumot ennek a levélívnek a címkéjének végére.
2. szabály. A β karakterlánc végétől induló út nem kezdődik S(i + 1)-el, de legalább egy út indul onnan. Ebben az esetben új levélívet kell létrehozni, a β végétől kezdve, S(i + 1) szimbólummal jelölve. Ebben az esetben, ha β az íven belül végződik, új csúcsot kell létrehozni. Az új levélív végén lévő levél j számot kap. Így a 2. szabályban két eset lehetséges.
3. szabály. A β karakterlánc végétől induló út az S(i + 1) szimbólummal kezdődik. Ebben az esetben a βS(i + 1) karakterlánc már az aktuális fában van, tehát semmit sem kell tenni (implicit utótagfában az utótag végét nem kell kifejezetten megjelölni).
Adott legyen a szöveg, és jöjjön egy sor minta a bemenetre. Miután Ukkonen algoritmussal utótagfát építettünk a szövegből, az egyes minták a következőképpen kereshetők:
|
Az utótagfát sztringek halmazára lehet építeni karakterlánc-összefűzéssel vagy anélkül.
|
Ez a megközelítés problémás a szintetikus utótagok jelenléte miatt, de ezt úgy oldják meg, hogy az ívekben az utótagpár második indexét a levélcsúcsokra redukáljuk.
Ebben az algoritmusban nem lesznek szintetikus utótagok.
|
Figyelembe kell venni, hogy a különböző íveken lévő tömörített címkék más-más sztringre vonatkozhatnak, ezért az íveken három számot kell tárolni.
Két karakterlánc utótagja megegyezhet, de ugyanakkor egyetlen utótag sem lesz másik utótag előtagja (az őrszem miatt). A levél ezután az utótag összes húrjára és kezdeti pozíciójára mutat.
A minták megtalálásának problémájának megoldására létezik az Aho-Korasik algoritmus. Megtalálja az összes előfordulást - a minták teljes hossza, T - a szöveg hossza, k - az előfordulások száma.
Aszimptotikus módon az összes előfordulás megtalálása egy utótagfában ugyanannyi időt vesz igénybe. De tény, hogy Aho-Korasik memóriát használ a kulcsfához , időt az építkezéshez és időt a kereséshez . De az utótag fa memóriát , időt - szerkesztést és keresést foglal el.
Vagyis ha sok a minta és több, mint a szöveg, akkor az utótag fa kicsi, de sokáig tart a keresés. Egyébként az Aho-Korasik, amikor a minták rövidek és a szöveg nagyobb, kevesebb memóriát foglal, de az utótagfát gyorsabban keresi.
Így az egyik vagy a másik közötti választás a határidőtől vagy a memóriától függ.
Fa (adatstruktúra) | |
---|---|
Bináris fák | |
Önkiegyensúlyozó bináris fák |
|
B-fák | |
előtag fák |
|
A tér bináris particionálása | |
Nem bináris fák |
|
A tér felosztása |
|
Más fák |
|
Algoritmusok |
|
Húrok | |
---|---|
Karakterlánc hasonlósági mértékek | |
Substring keresés | |
palindromák | |
Sorozat-igazítás | |
Utótag szerkezetek | |
Egyéb |