Et suffikstræ er et bor , der indeholder alle suffikserne af en eller anden streng (og kun dem). Giver dig mulighed for at finde ud af, om strengen w er inkluderet i den oprindelige streng t i O (|w|) tid , hvor |w| er længden af strengen w .
|
Lemma. Placeringen af w er eksplicit i et kompakt suffikstræ, hvis og kun hvis w er et ikke-indlejret suffiks af t eller w er en højre gren.
Bevis. . Hvis det er eksplicit, så kan det enten være et blad eller et grenspids eller en rod (i dette tilfælde er w også et indlejret suffiks af t ).
Hvis er et blad, så er det også et suffiks t . Så det må ikke være et indlejret suffiks, for ellers ville det optræde et andet sted i strengen t : v er et suffiks af t sådan at w er et præfiks til v . Denne node kan ikke være et blad.
Hvis er en grenknude, så skal der være mindst to udgående kanter fra med forskellige etiketter. Det betyder, at der er to forskellige suffikser u , v , at w er et præfiks for u og w er et præfiks til v , hvor v = wxs , u = , x . Derfor er w en højre gren.
. Hvis w er et ikke-indlejret suffiks af t , skal det være et blad. Hvis w er en højre gren, så er der to suffikser u og v , u = wxs , v = , x , så er w en grenknude. Lemmaet er bevist .
Nu er det let at se, hvorfor svaret på spørgsmålet om ordet w er i strengen t kan findes i O(|w|) tid : man behøver kun at tjekke om w er en placering (eksplicit eller implicit) i cst( t ).
Kantetiketterne skal være pointere til positionen i strengen, så suffikstræet bruger O(n) -hukommelse . Kantetiketten (p, q) betyder en understreng eller den tomme streng, hvis p > q.
Ukkonen introducerer navnet åbne kanter for kanter, der ender med blade. Åbne kantetiketter skrives som (p, ) i stedet for (p, |t|) , hvor er længden, altid større end |t| .
Lad T være et -træ. Lad være et knudepunkt T , v være det længste suffiks w sådan som også er en knude T . En umærket kant fra til kaldes et suffikslink . Hvis v = w , kaldes det atomare .
Udmelding. I ast( t ) og cst( t$ ), hvor $ t , er alle suffikslinks atomare.
Bevis. $ -symbolet kaldes vagtsymbolet . Den første del (for ast( t )) følger af definitionen, da placeringerne er eksplicitte. For at bevise den anden del (tilfælde cst( t )) skal vi vise, at for hver knude er også en knude cst( t ). Hvis er en knude cst( t ), så er det enten et blad eller en grenknude. Hvis er et blad, så er aw ikke et indlejret suffiks af t . Takket være vagtsymbolet følger det af lemmaet, at alle suffikser (inklusive roden, det tomme suffiks) er eksplicitte, da kun roden er et indlejret suffiks. Derfor er w et blad eller en rod. Hvis er en grenknude, så er aw en højre gren, ligesom w er det . Derfor er placeringen eksplicit ved lemmaet. Påstanden er blevet bevist.
Som det følger af dette bevis, garanterer vagtsymbolet eksistensen af blade for alle suffikser. Med et sådant tegn kan der ikke være andre indlejrede suffikser end tomme. Hvis vi udelader guard-karakteren, kan nogle suffikser blive indlejrede, og deres placeringer bliver implicitte.
Udmelding. Et kompakt suffikstræ kan repræsenteres i en form, der kræver O(n) -hukommelse.
Bevis. Endelsestræet indeholder højst et blad pr. endelse (nøjagtig et med vogterkarakter). Hver intern node skal være en grenknude, så en intern node har mindst to børn. Hver gren øger antallet af blade med mindst én, så vi har højst n interne noder og højst n blade.
For at repræsentere strenge, der er kantetiketter, bruger vi indeksering på kildestrengen, som beskrevet ovenfor. Hver node har højst én forælder, og det samlede antal kanter overstiger derfor ikke 2n .
På samme måde har hver node højst et suffikslink, så er det samlede antal suffikslinks også begrænset til 2n . Påstanden er blevet bevist.
Som et eksempel på et suffikstræ med 2n-1 noder kan du overveje træet for ordet . Størrelsen af det atomare suffikstræ for streng t er O( ) .
Mcc - algoritmen starter med et tomt træ og tilføjer suffikser startende fra det længste. Mcc - algoritmen er ikke en online-algoritme, det vil sige, at hele linjen er nødvendig for dens drift. Korrekt betjening kræver, at strengen afsluttes med et specialtegn, der adskiller sig fra de andre, således at intet suffiks er et præfiks til et andet suffiks. Hvert suffiks i træet svarer til et blad. For algoritmen definerer vi - det aktuelle suffiks (i trin ), ( head ) - det største præfiks for suffikset , som også er præfikset for et andet suffiks , hvor . ( hale ) definere som .
Nøgleideen med mcc- algoritmen er forholdet mellem og .
Lemma. Hvis hvor er et bogstav i alfabetet, er en streng (kan være tom), så er et præfiks .
Bevis. Lad . Så findes der , , sådan som både er et præfiks på og et præfiks på . Så er et præfiks og er derfor et præfiks for hovedet . Lemmaet er bevist.
Vi kender placeringen af , og hvis vi har et suffikslink, kan vi hurtigt hoppe til placeringen af hovedpræfikset uden at skulle finde stien fra træets rod. Men placeringen er muligvis ikke eksplicit (hvis placeringen ikke var eksplicit i det forrige trin), og suffikslinket er muligvis endnu ikke indstillet til node . McCrays løsning finder en node i to trin: "genscanning" og "scanning". Vi krydser træet op fra knudepunktet , indtil vi finder et suffikslink, følger det og scanner derefter stien til placeringen igen (hvilket er enkelt, fordi vi ved, at længden og placeringen eksisterer, så vi ikke behøver at læse hele kanten etiketter, der bevæger sig ned på træet, kan vi bare kontrollere kun begyndelsesbogstaverne og længden af ordene).
Figuren viser denne idé. I stedet for at forsøge at finde en sti fra roden til knudepunktet , springer algoritmen til , følger suffikset til , genscanner stien til den (muligvis implicitte) placering og forbliver for at finde stien til , krydsende tegn for tegn.
Algoritmen består af tre dele.
1. Først bestemmer den strukturen af det forrige hoved, finder det næste tilgængelige suffikslink og følger det.
2. Den scanner derefter den del af det forrige hoved, som længden er kendt for (denne del hedder ).
3. Til sidst sætter algoritmen suffikslinket for , scanner resten (navngivet ) og tilføjer et nyt blad for .
En grenknude oprettes i anden fase af genscanningen, hvis der ikke findes en lokation . I dette tilfælde er scanningen ikke nødvendig, for hvis den var længere end , så ville det være en højre gren, men ved lemmaet er det også en højre gren, så noden skal allerede eksistere. Noden oprettes i tredje fase, hvis placeringen ikke allerede er eksplicit.
Algoritme 1 (mcc, McCreight) Input: streng t 1: T : = tomt træ; 2: hoved 0 := ; 3: n := længde(t) ; 4: for i := 1 til n gør 5: find , , sådan at en. hoved i-1 = , b. hvis forfaderen til nodehovedet i -1 ikke er roden ( rod ), angiv den , ellers c. og ( | hoved i-1 |=0) 6: hvis ( >0) så 7: følg suffikslinket fra node til ; 8: end if 9: := Rescan( ) ; 10: sæt suffikslink fra til 11: ( , hale i ) := Scan ( , suf i - ); 12: tilføj et blad svarende til hale i ; 13: slut forBemærk, at hvis derefter og genkendes lige så hurtigt, som at følge suffikslinket i henhold til linje 7 i algoritmen.
Genscanningsproceduren leder efter en placering . Hvis placeringen endnu ikke er eksplicit, tilføjes en ny node. Dette tilfælde opstår, når hovedet ( ) ses i sin helhed: hvis hovedet er længere (og noden allerede er defineret), skal det være et præfiks med mere end to suffikser og er også en venstre gren af . En placering kan kun være eksplicit, hvis den knude allerede er en grenknude, og hvis der ikke var nogen venstre gren , må den have været længere, fordi der blev fundet et længere præfiks.
Scanningsproceduren søger i træets dybde og returnerer positionen.
Procedure 1 Rescan(n, ) Input : node n , linie 1: i :=1; 2: mens jeg | | gør 3: find kant e=n n' , w 1 = 1 ; 4: hvis jeg +| w |>| |+1 derefter 5: k :=| | -i +1; 6: delt kant e med ny knude m og kanter n m og m n' ; 7: retur m ; 8: slut hvis 9: i := i +| w |; 10: n := n' ; 11: ende mens 12: returnere n' ; Procedure 2 Scan(n, ) Input : node n , linie 1: i :=1; 2: mens der er en kant e = n n' , w 1 = i do 3: k :=1; 4: mens w k = i og k | w | til 5: k := k +1; 6: i := i +1; 7: slut mens 8: hvis k >| w | derefter 9: n := n' ; 10: andet 11: delt kant e med ny node m og kanter n m og m n' ; 12: return ( m , i ,..., ); 13: end if 14: end mens 15: return ( n , i ,..., );Algoritmen, som Esko Ukkonen opfandt til at bygge et suffikstræ i lineær tid, er sandsynligvis den enkleste af disse algoritmer. Enkelheden kommer af, at algoritmen indledningsvis kan opfattes som en simpel, men ineffektiv metode, der med nogle få "common-sense"-implementeringer når niveauet af de bedste algoritmer i form af køretid under værst tænkelige forhold. Det samme gøres i PDF med figurer .
Detaljeret forklaring af algoritmen og implementeringen i C++ : cs.mipt.ru (på russisk) og marknelson.us (på engelsk)
Til Ukkonen-algoritmen har vi brug for
1) Implicitte suffikstræer 2) Generel beskrivelse af algoritmen 3) AlgoritmeoptimeringUkkonens algoritme bygger en sekvens af implicitte suffikstræer, hvoraf det sidste konverteres til et ægte strengsuffikstræ S .
Неявное суффиксное дерево строки S — это дерево, полученное из суффиксного дерева S$ удалением всех вхождений терминального символа $ из меток дуг дерева, удалением после этого дуг без меток и удалением затем вершин, имеющих меньше двух детей. Неявное суффиксное дерево префикса S[l..i] строки S аналогично получается из суффиксного дерева для S[l..i]$ удалением символов $, дуг и вершин, как описано выше.
Det implicitte suffikstræ for enhver streng S vil have færre blade end suffikstræet for streng S$, hvis og kun hvis mindst et af suffikserne af S er et præfiks for et andet suffiks. Terminalen $ blev tilføjet til slutningen af S bare for at undgå denne situation. Ved at definere et rigtigt suffikstræ er dette et meget vigtigt punkt. Men hvis S slutter med et tegn, der ikke optræder andre steder i S, så vil det implicitte suffikstræ for S have et blad for hvert suffiks og er derfor et sandt suffikstræ.
Selvom det implicitte suffikstræ måske ikke har blade for alle suffikser, koder det alle suffikserne S - hver udtales af tegnene på en sti fra roden af dette implicitte suffikstræ. Men hvis denne sti ikke ender med et blad, vil der ikke være nogen markør, der angiver slutningen af stien. Således er implicitte suffikstræer i sig selv noget mindre informative end rigtige. Vi vil kun bruge dem som en hjælp til Ukkonens algoritme for at få et sandt suffikstræ for S.
Ukkonens algoritme bygger et implicit suffikstræ T i for hvert præfiks S[l..i] af streng S, startende ved T 1 og inkrementering i med én, indtil T m er bygget . Det rigtige suffikstræ for S kommer fra T m , og hele jobbet tager O(m) tid. Vi vil forklare Ukkonens algoritme ved først at præsentere en metode, hvorved alle træer bygges i O(m³) tid, og derefter vil vi optimere implementeringen af denne metode, så den deklarerede hastighed opnås.
For at gøre denne generelle beskrivelse til en algoritme, skal vi specificere præcis, hvordan vi udfører suffiksfortsættelse. Lad S[j..i] = β være suffikset af S[1..i]. I fortsættelse j, når algoritmen finder enden β i det aktuelle træ, fortsætter den β for at sikre, at suffikset βS(i + 1) er til stede i træet. Algoritmen fungerer efter en af følgende tre regler.
Regel 1. I det aktuelle træ ender stien β i et blad. Det betyder, at stien fra roden mærket β går til enden af en "blad"-bue (den bue, der er inkluderet i bladet). Når du skifter træ, skal du tilføje symbolet S(i + 1) til enden af etiketten på denne bladbue.
Regel 2. Ingen sti fra enden af strengen β begynder med S(i + 1), men der er mindst én sti, der starter derfra. I dette tilfælde skal der oprettes en ny bladbue, startende i slutningen af β, mærket med symbolet S(i + 1). I dette tilfælde, hvis β ender inde i buen, skal der oprettes et nyt toppunkt. Bladet for enden af den nye bladbue tildeles nummeret j. I regel 2 er der således to tilfælde mulige.
Regel 3. En sti fra enden af strengen β begynder med symbolet S(i + 1). I dette tilfælde er strengen βS(i + 1) allerede i det aktuelle træ, så der skal ikke gøres noget (i et implicit suffikstræ behøver slutningen af suffikset ikke at være markeret eksplicit).
Lad teksten være givet og et sæt mønstre kommer til input. Efter at have bygget et suffikstræ fra teksten ved hjælp af Ukkonen-algoritmen, kan hvert mønster søges på følgende måde:
|
Et suffikstræ kan bygges på et sæt strenge med eller uden strengsammenkædning.
|
Denne tilgang er problematisk på grund af tilstedeværelsen af syntetiske suffikser, men dette løses ved at reducere det andet indeks af suffiksparret i buerne til bladhjørnerne.
Der vil ikke være nogen syntetiske suffikser i denne algoritme.
|
Det skal tages i betragtning, at de komprimerede etiketter på forskellige buer kan referere til forskellige strenge, så tre numre skal gemmes på buerne.
Suffikser for to strenge kan matche, men på samme tid vil intet suffiks være et præfiks for et andet suffiks (på grund af vagtpost). Bladet peger derefter på alle strenge og startpositioner for det suffiks.
For at løse problemet med at finde et sæt mønstre er der Aho-Korasik-algoritmen. Den finder alle forekomster for - den samlede længde af mønstrene, T - længden af teksten, k - antallet af forekomster.
Asymptotisk tager det lige lang tid at finde alle forekomster i et suffikstræ. Men faktum er, at Aho-Korasik bruger hukommelse til nøgletræet , tid til at bygge og tid til at søge . Men suffikstræet optager hukommelse , tid - konstruktion og søgning.
Det vil sige, at hvis der er mange prøver og mere end teksten, så er suffikstræet lille, men det tager lang tid at søge. Ellers tager Aho-Korasik, når mønstrene er korte og teksten er større, mindre hukommelse, men suffikstræet søges hurtigere.
Således afhænger valget mellem det ene eller det andet af grænsetiden eller hukommelsen.
Træ (datastruktur) | |
---|---|
Binære træer | |
Selvbalancerende binære træer |
|
B-træer | |
præfiks træer |
|
Binær opdeling af rummet | |
Ikke-binære træer |
|
At bryde rummet op |
|
Andre træer |
|
Algoritmer |
|
Strenge | |
---|---|
String lighedsmål | |
Understrengssøgning | |
palindromer | |
Sekvensjustering | |
Suffiksstrukturer | |
Andet |