Zeichenkettenalgorithmus

Berechnungsmethoden die auf Zeichenketten arbeiten From Wikipedia, the free encyclopedia

Zeichenkettenalgorithmen (englisch: string algorithms) sind Verfahren der Informatik zur algorithmischen Bearbeitung endlicher Symbolsequenzen über einem gegebenen Alphabet. Sie umfassen Methoden zur Lokalisation von Teilsequenzen, zur Generierung komprimierter Indizes sowie zur Transformation und Analyse digitaler Texte. Ihre Anwendung erstreckt sich auf Bereiche wie die Bioinformatik, Datenkompression, Plagiaterkennung und das Information Retrieval.

Grundbegriffe

Alphabet

Ein Alphabet ist eine endliche Menge von Symbolen.[1] Die Symbole eines Alphabets werden auch als Zeichen (englisch: characters) bezeichnet.[2] Häufig verwendete Alphabete umfassen das binäre Alphabet , das ASCII-Alphabet mit 128 Zeichen oder das Unicode-Alphabet mit über 100.000 Zeichen.[1]

Zeichenkette

Eine Zeichenkette (englisch: string) ist eine endliche Sequenz von Symbolen über einem Alphabet.[1] Eine Zeichenkette wird typischerweise als geschrieben, wobei jedes ein Symbol aus dem zugrunde liegenden Alphabet ist.[3] Die Länge einer Zeichenkette ist definiert als die Anzahl der Symbole in dieser Sequenz und wird mit notiert.[1]

Teilzeichenkette, Präfix und Suffix

Eine Teilzeichenkette (englisch: substring) ist eine zusammenhängende Teilfolge von Zeichen innerhalb einer Zeichenkette.[4] Ein Präfix ist eine Teilzeichenkette, die am Anfang einer Zeichenkette beginnt, also bei Index 0 startet.[5] Ein Suffix ist eine Teilzeichenkette, die am Ende einer Zeichenkette endet.[6] Formal gilt: Ein String ist ein Präfix von , wenn ein String existiert, sodass gilt. Entsprechend ist ein Suffix von , wenn ein String existiert, sodass gilt.[4]

Konkatenation

Die Konkatenation ist die Verkettung zweier Zeichenketten, bei der die zweite Zeichenkette an das Ende der ersten angefügt wird. Für zwei Zeichenketten und wird ihre Konkatenation als notiert. Die Länge der konkatenierten Zeichenkette entspricht der Summe der Längen der beiden Ausgangszeichenketten: .[1]

Historische Entwicklung

Die systematische Erforschung von Zeichenkettenalgorithmen begann in den späten 1960er Jahren mit der Veröffentlichung des ersten Bandes von The Art of Computer Programming durch Donald E. Knuth im Jahr 1968. Dieses Werk legte die theoretischen Grundlagen für die Analyse von Algorithmen und etablierte den Begriff der asymptotischen Komplexität in der Informatik.[7][8]

Im Jahr 1970 veröffentlichten James H. Morris jr. und Vaughan R. Pratt einen Technical Report, der die Grundlage für den später nach ihnen und Knuth benannten Algorithmus legte.[9][10] Die endgültige Version des Knuth-Morris-Pratt-Algorithmus erschien 1977 in der Fachzeitschrift SIAM Journal on Computing und stellte den ersten linearen String-Matching-Algorithmus dar.[11][12]

Im selben Jahr 1977 wurde der Boyer-Moore-Algorithmus von Robert S. Boyer und J Strother Moore vorgestellt, der durch seine rückwärts gerichtete Suche und seine Verschiebungsheuristiken eine deutliche Beschleunigung in der Praxis ermöglichte.[13][14]

Der Rabin-Karp-Algorithmus, der auf dem Prinzip des Fingerprinting basiert, wurde 1980 entwickelt und nutzt Rolling-Hash-Funktionen für effiziente Mustersuche.[15]

Ein bedeutender Meilenstein in der Entwicklung von Datenstrukturen für Zeichenketten war die Einführung der Suffixarrays durch Udi Manber und Eugene Myers im Jahr 1993. Diese Datenstruktur bot eine platzsparende Alternative zu Suffixbäumen.[16][17]

Die erste lineare Konstruktion von Suffixarrays wurde erst im Jahr 2003 erreicht, als mehrere Forschergruppen unabhängig voneinander lineare Algorithmen entwickelten.[18]

Algorithmen für das String-Matching

Das String-Matching-Problem, auch als Mustersuche (englisch: pattern matching) bezeichnet, besteht darin, alle Vorkommen eines Musters in einem Text zu finden.[4] Die Länge des Textes wird typischerweise mit und die Länge des Musters mit bezeichnet.[19]

Hauptalgorithmen

Knuth-Morris-Pratt-Algorithmus

Der Knuth-Morris-Pratt-Algorithmus (KMP-Algorithmus) wurde 1977 von Donald E. Knuth, James H. Morris und Vaughan R. Pratt veröffentlicht. Der Algorithmus vergleicht das Muster von links nach rechts mit dem Text. Die zentrale Innovation des KMP-Algorithmus besteht in der Verwendung einer Präfixfunktion (auch Failure-Funktion genannt), die für jede Position im Muster die Länge des längsten echten Präfix angibt, das auch Suffix des aktuellen Präfixes des Musters ist.[12]

Der KMP-Algorithmus weist eine lineare Zeitkomplexität von auf.[12] Bei der Suche werden höchstens Textzeichenvergleiche durchgeführt.[20] Die Raumkomplexität beträgt für die Speicherung der Präfixfunktion.[12]

Boyer-Moore-Algorithmus

Der Boyer-Moore-Algorithmus, 1977 von Robert S. Boyer und J Strother Moore veröffentlicht, gilt als einer der effizientesten String-Matching-Algorithmen in typischen Anwendungen.[21] Im Gegensatz zum KMP-Algorithmus vergleicht der Boyer-Moore-Algorithmus das Muster von rechts nach links.[22]

Der Algorithmus verwendet zwei Verschiebungsfunktionen: die Bad-Character-Heuristik und die Good-Suffix-Heuristik. Die Bad-Character-Heuristik verschiebt das Muster basierend auf dem rechts vom aktuellen Fenster stehenden Zeichen, während die Good-Suffix-Heuristik die Information über übereinstimmende Suffixe nutzt.[23]

In der Praxis erreicht der Boyer-Moore-Algorithmus oft eine sublineare Laufzeit, da bei großen Alphabeten viele Zeichen des Textes übersprungen werden können. Die Worst-Case-Komplexität beträgt , während die Best-Case-Komplexität beträgt.[24] Die Raumkomplexität liegt bei , wobei die Größe des Alphabets bezeichnet.

Rabin-Karp-Algorithmus

Der Rabin-Karp-Algorithmus wurde 1987 von Richard M. Karp und Michael O. Rabin entwickelt. Der Algorithmus verwendet das Prinzip des Rolling-Hash für effiziente Mustersuche.[15] Dabei wird für das Muster und für jedes Fenster der Länge im Text ein Hashwert berechnet.[25] Statt direkter Zeichenvergleiche werden zunächst die Hashwerte verglichen, und nur bei Übereinstimmung erfolgt ein exakter Vergleich.[26]

Der Rabin-Karp-Algorithmus ist besonders effektiv für die Mehrfachmustersuche, bei der mehrere Muster gleichzeitig gesucht werden.[26] Die durchschnittliche Zeitkomplexität beträgt , während der Worst-Case beträgt.[27] Die Raumkomplexität ist , da keine zusätzlichen Datenstrukturen benötigt werden.[25]

Aho-Corasick-Algorithmus

Der Aho-Corasick-Algorithmus wurde 1975 von Alfred V. Aho und Margaret J. Corasick entwickelt. Er löst das Problem des Mehrfachmuster-Matchings, bei dem mehrere Muster gleichzeitig in einem Text gesucht werden.[28]

Der Algorithmus basiert auf einem endlichen Automaten, der aus einem Trie der Muster mit zusätzlichen Failure-Links konstruiert wird. Die Zeitkomplexität beträgt , wobei die Anzahl der gefundenen Übereinstimmungen ist.[28] Die Raumkomplexität liegt bei , wobei die maximale Musterlänge ist.

Anwendungen des Aho-Corasick-Algorithmus umfassen die Tokenisierung von Texten und die Erkennung von Eindringlingen in Netzwerken (Intrusion Detection).[29]

Weitere String-Matching-Algorithmen

Neben den genannten Hauptalgorithmen existieren zahlreiche weitere Verfahren für das String-Matching:

Der Horspool-Algorithmus (1980) stellt eine vereinfachte Variante des Boyer-Moore-Algorithmus dar, die ausschließlich die Bad-Character-Heuristik verwendet.[30] Der Sunday-Algorithmus (1990), auch als „Quick-Search“ bekannt, nutzt das Zeichen unmittelbar nach dem aktuellen Suchfenster für die Verschiebungsberechnung.[31][32] Der Zhu-Takaoka-Algorithmus (1987) erweitert die Bad-Character-Heuristik auf zwei Zeichen.[33] Der Turbo-BM-Algorithmus (1992) führt den Turbo-Shift ein, der den Worst-Case des Boyer-Moore-Algorithmus verbessert.[19] Der Apostolico-Giancarlo-Algorithmus (1986) bietet eine einfache lineare Schranke für das String-Matching.[34] Der Two-Way-Algorithmus (1991) arbeitet in-place mit -Zeitkomplexität und wird in der GNU-C-Bibliothek (glibc) verwendet.[35] Der Commentz-Walter-Algorithmus (1979) kombiniert die Strategien von Boyer-Moore und Aho-Corasick für die Mehrfachmustersuche.[13][36] Die Shift-Or- und Shift-And-Algorithmen (1992) nutzen bit-parallele Operationen für das String-Matching.[37]

Datenstrukturen

Suffixbäume

Ein Suffixbaum ist eine komprimierte Trie-Datenstruktur, die alle Suffixe einer gegebenen Zeichenkette speichert.[38] Die Entwicklung von Suffixbäumen begann mit Peter Weiner, der 1973 den ersten Linearzeitalgorithmus zur Konstruktion vorstellte.[39] Der Weiner-Algorithmus hat eine Zeitkomplexität von , benötigt jedoch Speicherplatz von Speicherplatz.[40]

Edward McCreight verbesserte 1976 den Algorithmus und reduzierte den Speicherbedarf auf .[40] Der McCreight-Algorithmus ist platzsparender als der Weiner-Algorithmus, arbeitet jedoch offline und benötigt den gesamten Text im Voraus.[39] Esko Ukkonen entwickelte 1995 einen Online-Algorithmus zur Suffixbaum-Konstruktion mit -Speicherbedarf.[41] Der Ukkonen-Algorithmus baut den Suffixbaum schrittweise auf und kann Zeichen für Zeichen verarbeiten.[42] Eine umfassende Darstellung von Algorithmen auf Suffixbäumen und verwandten Datenstrukturen bietet das Lehrbuch Algorithms on Strings, Trees, and Sequences: Computer Science and Computational Biology von Dan Gusfield aus dem Jahr 1997.[40]

Suffixarrays

Ein Suffixarray ist eine sortierte Liste aller Suffixe einer Zeichenkette, die als Array von Startindizes repräsentiert wird.[16] Die Einführung von Suffixarrays erfolgte 1993 durch Udi Manber und Eugene Myers.[17] Der Manber-Myers-Algorithmus zur Konstruktion hat eine Zeitkomplexität von , während die Suche nach einem Muster Zeit von benötigt.[16]

Die Entwicklung linearer Konstruktionsalgorithmen für Suffixarrays war ein bedeutender Forschungsschwerpunkt.[16] Erst 2003 wurden mehrere unabhängige Algorithmen mit linearer Laufzeit vorgestellt.[18] Ein eleganter Algorithmus zur Konstruktion von Suffixarrays wurde von Rajasekaran und Nicolae 2014 beschrieben.[43]

Tries und Patricia-Trees

Ein Trie (auch Präfixbaum genannt) ist eine baumbasierte Datenstruktur zur Speicherung von Zeichenketten, bei der jeder Knoten ein Zeichen repräsentiert.[44] Tries werden für Autocomplete-Funktionen, Rechtschreibprüfungen und IP-Routing eingesetzt.[45]

Der Patricia-Trie (Practical Algorithm To Retrieve Information Coded In Alphanumeric) wurde 1968 von Donald R. Morrison vorgestellt.[46] Patricia-Tries sind platzoptimierte Varianten von Tries, die Pfadkomprimierung verwenden und -Speicher benötigen.[47][48]

Burrows-Wheeler-Transformation

Die Burrows-Wheeler-Transformation (BWT) wurde 1994 von Michael Burrows und David Wheeler am DEC Systems Research Center (SRC) in Palo Alto entwickelt.[49] Die BWT ist eine reversible Transformation, die eine Zeichenkette in eine permutierte Form überführt, in der gleiche Zeichen typischerweise gruppiert auftreten.[50] Diese Eigenschaft macht die BWT besonders geeignet für die Datenkompression.[51]

Die BWT bildet die Grundlage für viele moderne Kompressionsalgorithmen, darunter bzip2.[52] Verbesserungen der Burrows-Wheeler-Kompression wurden von verschiedenen Forschern vorgeschlagen.[49]

FM-Index

Der FM-Index wurde 2000 von Paolo Ferragina und Giovanni Manzini entwickelt. Der FM-Index ist ein komprimierter Volltext-Index, der die Burrows-Wheeler-Transformation mit Rang- und Selektionsoperationen kombiniert. Der Index benötigt etwa 0,5 Bytes pro Zeichen und ermöglicht effiziente Suchoperationen.[53]

Der FM-Index wird in der Bioinformatik für die Sequenzalignment eingesetzt, insbesondere in Tools wie Bow-Tie und Burrows-Wheeler-Aligner (BWA).[54]

Komplexitätsanalyse

Zeitkomplexität

Die Zeitkomplexität von String-Matching-Algorithmen beschreibt die Anzahl der benötigten Operationen in Abhängigkeit von der Textlänge und der Musterlänge .[19]

Der naive Algorithmus hat eine Best-Case-Komplexität von , eine Average-Case-Komplexität von und eine Worst-Case-Komplexität von .[55] Der Knuth-Morris-Pratt-Algorithmus garantiert eine Zeitkomplexität von in allen Fällen.[56] Der Boyer-Moore-Algorithmus erreicht im Best-Case , im Average-Case ebenfalls und im Worst-Case .[57] Der Rabin-Karp-Algorithmus hat eine Best- und Average-Case-Komplexität von und einen Worst-Case von .[27] Die Z-Algorithmus bietet eine Zeitkomplexität von .[58] Der Aho-Corasick-Algorithmus arbeitet in , wobei die Anzahl der Ausgaben ist.[59]

Raumkomplexität

Die Raumkomplexität beschreibt den zusätzlichen Speicherbedarf neben dem Eingabetext und dem Muster.

Der naive Algorithmus und der Rabin-Karp-Algorithmus benötigen zusätzlichen Speicher von .[60] Der Knuth-Morris-Pratt-Algorithmus benötigt Speicher von für die Präfixfunktion.[61] Der Boyer-Moore-Algorithmus benötigt Speicher von für die Verschiebungstabellen.[57] Der Z-Algorithmus benötigt Speicher von für das Z-Array.[58] Der Aho-Corasick-Algorithmus benötigt Speicher von für den Automaten.[62]

Untere Schranken

Ronald L. Rivest bewies 1977, dass für das String-Matching mindestens Zeichenabfragen von notwendig sind. Diese untere Schranke gilt für deterministische Algorithmen im Worst-Case.[63]

Zsolt Tuza zeigte, dass mehr als 50 Prozent aller binären Muster „evasive“ sind, was bedeutet, dass alle Zeichen abgefragt werden müssen.[64]

Anwendungsgebiete

Bioinformatik

Die Bioinformatik ist eines der wichtigsten Anwendungsgebiete für Zeichenkettenalgorithmen.[65] Die Analyse biologischer Sequenzen wie DNA, RNA und Proteine erfordert effiziente Algorithmen für das Muster-Matching und das Sequenzalignment.[66]

Der Needleman-Wunsch-Algorithmus, 1970 entwickelt, löst das Problem des globalen Alignments zweier Sequenzen mit einer Zeitkomplexität von .[67][68] Der Smith-Waterman-Algorithmus, 1981 veröffentlicht, berechnet lokale Alignments zwischen Sequenzen.[69] Die Burrows-Wheeler-Transformation und der FM-Index bilden die Grundlage für moderne Sequenzierungswerkzeuge wie Bow-Tie und BWA.[53] Diese Tools ermöglichen die effiziente Kartierung von kurzen DNA-Sequenzen auf Referenzgenome.[70]

Textverarbeitung

In der Textverarbeitung werden Zeichenkettenalgorithmen für Suchen-und-Ersetzen-Funktionen, reguläre Ausdrücke und Texteditoren eingesetzt.

Der Boyer-Moore-Algorithmus ist etwa drei bis fünf Mal schneller als der Knuth-Morris-Pratt-Algorithmus (KMP) in typischen Anwendungen und wird im GNU-grep-Programm verwendet.[71] Die Implementierung des KMP-Algorithmus wird in verschiedenen akademischen Kursen behandelt.[19]

Datenkompression

Die Datenkompression nutzt Zeichenkettenalgorithmen zur Reduktion der Speichergröße von Texten.

Die Burrows-Wheeler-Transformation ist eine Kernkomponente des Kompressionsprogramms bzip2.[72] Die Lempel-Ziv-Kompressionsalgorithmen (LZ77 und LZ78), 1977 und 1978 entwickelt,[73] bilden die Grundlage für viele moderne Kompressionsformate wie ZIP und Gzip.[74]

Information Retrieval

Im Information Retrieval werden Zeichenkettenalgorithmen für die Indexierung und Suche in großen Textsammlungen eingesetzt.[75]

Suffixarrays und Suffixbäume ermöglichen effiziente Volltextsuchen in großen Dokumentensammlungen.[17] Tries werden für Autocomplete-Funktionen, Rechtschreibprüfungen und IP-Routing-Tabellen verwendet.[76] Das approximative String-Matching ermöglicht die Suche nach ähnlichen Zeichenketten und wird für fehlertolerante Suchen eingesetzt.[77]

Plagiaterkennung

Die Plagiaterkennung nutzt Zeichenkettenalgorithmen zur Identifikation von Textüberlappungen und ähnlichen Dokumenten.

Der Rabin-Karp-Algorithmus mit Rolling-Hash wird für die effiziente Erkennung von Textplagiaten eingesetzt.[78] Der Winnowing-Algorithmus basiert auf k-gram-Hashes und wird für die Dokumentensignatur verwendet.[79] String-Ähnlichkeitsmaße wie Tf-idf, Cosine-Similarity, Jaro-Winkler-Distanz und Levenshtein-Distanz werden für die Plagiaterkennung eingesetzt.[80]

Forensik

Weitere Anwendungen der Zeichenkettenalgorithmen in der Forensik umfassen die Restricted Forensic Levenshtein Distance für die Analyse von DNA-Spuren.[81]

Einzelnachweise

Related Articles

Wikiwand AI