Utilisation de indexOf pour rechercher toutes les occurrences d’un mot dans une chaîne

Utilisation de indexOf pour rechercher toutes les occurrences d'un mot dans une chaîne

1. Vue d'ensemble

La corvée consistant à rechercher un motif de caractères, ou un mot, dans une chaîne de texte plus grande s'effectue dans divers champs. En bioinformatique, par exemple, il peut être nécessaire de trouver un extrait d’ADN dans un chromosome.

Dans les médias, les éditeurs localisent une phrase particulière dans un texte volumineux. La surveillance des données détecte les escroqueries ou les spams en recherchant les mots suspects intégrés aux données.

Dans tous les contextes, la recherche est si connue et si intimidante qu'elle est communément appeléethe “Needle in a Haystack Problem”. Dans ce didacticiel, nous allons démontrer un algorithme simple qui utilise la méthodeindexOf(String str, int fromIndex) de la classe JavaString pour rechercher toutes les occurrences d'un mot dans une chaîne.

2. Algorithme simple

Au lieu de simplement compter les occurrences d'un mot dans un texte plus volumineux, notre algorithme détecte et identifie chaque emplacement où un mot spécifique existe dans le texte. Notre approche du problème est courte et simple pour que:

  1. La recherchewill find the word even within words in the text. Par conséquent, si nous recherchons le mot «capable», nous le trouverons entre «confortable» et «tablette».

  2. La recherchewill be case-insensitive.

  3. L'algorithmeis based on the naïve string search approach. Cela signifie que, puisque nous sommes naïfs sur la nature des caractères du mot et de la chaîne de texte, nous utiliserons la force brute pour vérifier chaque emplacement du texte pour une instance du mot recherché.

2.1. la mise en oeuvre

Maintenant que nous avons défini les paramètres de notre recherche, écrivons une solution simple:

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. Tester la solution

Pour tester notre algorithme, nous allons utiliser un extrait d’un passage célèbre du Hamlet de Shakespeare et rechercher le mot «ou», qui apparaît cinq fois:

@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);
}

Lorsque nous exécutons notre test, nous obtenons le résultat attendu. 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"

En termes mathématiques, l'algorithme a une notation Big-O deO(m*(n-m)), oùm est la longueur du mot etn est la longueur de la chaîne de texte. Cette approche peut convenir à des chaînes de texte contenant plusieurs milliers de caractères, mais sera extrêmement lente s'il existe des milliards de caractères.

3. Algorithme amélioré

L'exemple simple ci-dessus illustre une approche naïve et brutale de la recherche d'un mot donné dans une chaîne de texte. En tant que tel, cela fonctionnera pour tout mot de recherche et tout texte.

Si nous savons à l'avance que le mot recherché ne contient pas de motif répétitif de caractères, tel que «aaa», alors nous pouvons écrire un algorithme légèrement plus efficace.

Dans ce cas, nous pouvons éviter en toute sécurité de faire la sauvegarde pour vérifier chaque emplacement de la chaîne de texte en tant qu'emplacement de départ potentiel. Après avoir appelé la méthodeindexOf( ), nous allons simplement glisser vers l'emplacement juste après la fin de la dernière occurrence trouvée. Ce simple ajustement donne un meilleur scénario deO(n).

Examinons cette version améliorée de l'ancienne méthodefindWord( ).

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. Conclusion

Dans ce tutoriel, nous avons présenté un algorithme de recherche ne faisant pas la distinction entre les majuscules et les minuscules pour rechercher toutes les variantes d'un mot dans une chaîne de texte plus grande. Mais ne laissez pas cela masquer le fait que la méthodeindexOf() de la classe JavaString est intrinsèquement sensible à la casse et peut faire la distinction entre «Bob» et «bob», par exemple.

Dans l'ensemble,indexOf() est une méthode pratique pour trouver une séquence de caractères enfouie dans une chaîne de texte sans faire de codage pour les manipulations de sous-chaînes.

Comme d'habitude, la base de code complète de cet exemple estover on GitHub.