Verwenden von indexOf zum Suchen aller Vorkommen eines Wortes in einer Zeichenfolge

Verwenden von indexOf zum Suchen aller Vorkommen eines Wortes in einer Zeichenfolge

1. Überblick

Die Suche nach einem Zeichenmuster oder einem Wort in einer größeren Textfolge wird in verschiedenen Feldern durchgeführt. In der Bioinformatik müssen wir beispielsweise möglicherweise ein DNA-Snippet in einem Chromosom finden.

In den Medien finden die Redakteure eine bestimmte Phrase in einem umfangreichen Text. Die Datenüberwachung erkennt Betrug oder Spam, indem sie nach verdächtigen Wörtern sucht, die in Daten eingebettet sind.

In jedem Kontext ist die Suche so bekannt und entmutigend, dass sie im Volksmundthe “Needle in a Haystack Problem” genannt wird. In diesem Tutorial zeigen wir einen einfachen Algorithmus, der dieindexOf(String str, int fromIndex) -Smethod der JavaString-Klasse verwendet, um alle Vorkommen eines Wortes in einer Zeichenfolge zu finden.

2. Einfacher Algorithmus

Anstatt einfach die Vorkommen eines Wortes in einem größeren Text zu zählen, wird mit unserem Algorithmus jede Stelle gefunden und identifiziert, an der ein bestimmtes Wort im Text vorhanden ist. Unsere Herangehensweise an das Problem ist kurz und einfach, so dass:

  1. Die Suchewill find the word even within words in the text. Wenn wir also nach dem Wort "fähig" suchen, finden wir es in "komfortabel" und "Tablet".

  2. Die Suchewill be case-insensitive.

  3. Der Algorithmusis based on the naïve string search approach. Dies bedeutet, dass wir, da wir hinsichtlich der Art der Zeichen im Wort und in der Textzeichenfolge naiv sind, Brute Force anwenden, um jede Position des Textes auf eine Instanz des Suchworts zu überprüfen.

2.1. Implementierung

Nachdem wir die Parameter für unsere Suche definiert haben, schreiben wir eine einfache Lösung:

public class WordIndexer {

    public List findWord(String textString, String word) {
        List indexes = new ArrayList();
        String lowerCaseTextString = textString.toLowerCase();
        String lowerCaseWord = word.toLowerCase();

        int index = 0;
        while(index != -1){
            index = lowerCaseTextString.indexOf(lowerCaseWord, index);
            if (index != -1) {
                indexes.add(index);
                index++;
            }
        }
        return indexes;
    }
}

2.2. Testen der Lösung

Um unseren Algorithmus zu testen, verwenden wir einen Ausschnitt einer berühmten Passage aus Shakespeares Hamlet und suchen nach dem Wort "oder", das fünfmal vorkommt:

@Test
public void givenWord_whenSearching_thenFindAllIndexedLocations() {
    String theString;
    WordIndexer wordIndexer = new WordIndexer();

    theString = "To be, or not to be: that is the question: "
      + "Whether 'tis nobler in the mind to suffer "
      + "The slings and arrows of outrageous fortune, "
      + "Or to take arms against a sea of troubles, "
      + "And by opposing end them? To die: to sleep; "
      + "No more; and by a sleep to say we end "
      + "The heart-ache and the thousand natural shocks "
      + "That flesh is heir to, 'tis a consummation "
      + "Devoutly to be wish'd. To die, to sleep; "
      + "To sleep: perchance to dream: ay, there's the rub: "
      + "For in that sleep of death what dreams may come,";

    List expectedResult = Arrays.asList(7, 122, 130, 221, 438);
    List actualResult = wordIndexer.findWord(theString, "or");
    assertEquals(expectedResult, actualResult);
}

Wenn wir unseren Test durchführen, erhalten wir das erwartete Ergebnis. Searching for “or” yields five instances embedded in various ways in the text string:

index of 7, in "or"
index of 122, in "fortune"
index of 130, in "Or
index of 221, in "more"
index of 438, in "For"

In mathematischen Begriffen hat der Algorithmus eine Big-O-Notation vonO(m*(n-m)), wobeim die Länge des Wortes undn die Länge der Textzeichenfolge ist. Dieser Ansatz eignet sich möglicherweise für Heuhaufen-Textzeichenfolgen mit einigen tausend Zeichen, ist jedoch bei Milliarden von Zeichen unerträglich langsam.

3. Verbesserter Algorithmus

Das obige einfache Beispiel zeigt einen naiven, gewaltsamen Ansatz für die Suche nach einem bestimmten Wort in einer Textzeichenfolge. Als solches funktioniert es für jedes Suchwort und jeden Text.

Wenn wir im Voraus wissen, dass das Suchwort kein sich wiederholendes Zeichenmuster wie „aaa“ enthält, können wir einen etwas effizienteren Algorithmus schreiben.

In diesem Fall können wir sicher vermeiden, dass die Sicherung durchgeführt wird, um jeden Speicherort in der Textzeichenfolge erneut als potenziellen Startspeicherort zu überprüfen. Nachdem wir die MethodeindexOf( )aufgerufen haben, werden wir einfach kurz nach dem Ende des zuletzt gefundenen Vorkommens zum Speicherort verschoben. Diese einfache Optimierung ergibt ein Best-Case-Szenario vonO(n).

Schauen wir uns diese erweiterte Version der früherenfindWord( )-Methode an.

public List findWordUpgrade(String textString, String word) {
    List indexes = new ArrayList();
    StringBuilder output = new StringBuilder();
    String lowerCaseTextString = textString.toLowerCase();
    String lowerCaseWord = word.toLowerCase();
    int wordLength = 0;

    int index = 0;
    while(index != -1){
        index = lowerCaseTextString.indexOf(lowerCaseWord, index + wordLength);  // Slight improvement
        if (index != -1) {
            indexes.add(index);
        }
        wordLength = word.length();
    }
    return indexes;
}

4. Fazit

In diesem Tutorial haben wir einen Suchalgorithmus vorgestellt, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird, um alle Variationen eines Wortes in einer größeren Textfolge zu finden. Lassen Sie sich jedoch nicht davon abhalten, dass die MethodeindexOf()der JavaString-Klasse von Natur aus zwischen Groß- und Kleinschreibung unterscheidet und beispielsweise zwischen „Bob“ und „Bob“ unterscheiden kann.

Insgesamt istindexOf() eine bequeme Methode, um eine in einer Textzeichenfolge vergrabene Zeichenfolge zu finden, ohne Codierungen für Teilstring-Manipulationen vorzunehmen.

Wie üblich ist die vollständige Codebasis dieses Beispielsover on GitHub.