Introduction aux chaînes de caractères
Les chaînes de caractères, souvent appelées strings, se distinguent des listes linéaires par leur focalisation sur des ensembles de caractères. Contrairement aux listes qui traitent des éléments individuels, les chaînes permettent principalement des opérations sur des sous-chaînes, c'est-à-dire des segments de caractères au sein d'une chaîne principale.
À noter qu'un tableau de caractères (comme char tableau[] = "salut") n'est pas équivalent à une chaîne de caractères (comme string chaine("salut")). L'utilisation de sizeof sur ces objets révèle des différences, car une chaîne encapsule un tableau avec des fonctions d'opérations supplémentaires.
Comparaison des chaînes
L'égalité entre chaînes requiert une longueur identique et une séquence de caractères identique. Pour l'ordre, la comparaison se base sur la position lexicographique des caractères et la longueur. Par exemple, "abc" est inférieur à "aeb" car 'b' précède 'e' dans l'ordre ASCII, tandis que "aaa" est supérieur à "aa" en raison de sa longueur.
Structures de stockage
Stockage séquentiel
Les chaînes sont stockées dans des tableaux à longueur fixe, où la longueur effective est souvent enregistrée dans le premier élément, par exemple longueur = tableau[0].
Stockage chaîné
Similaire à une liste chaînée, mais chaque nœud peut contenir un ou plusieurs caractères pour optimiser l'espace, évitant ainsi une surcharge due à un nœud par caractère.
Utilisation en C++
En C++, la bibliothèque <string> fournit la classe string, qui offre de nombreuses fonctionnalités intégrées.
Constructeurs de chaînes
| Fonction | Description |
|---|---|
string() |
Crée une chaîne vide. |
string(const char* s) |
Constructeur avec argument C-string. |
string(size_t n, char c) |
Crée une chaîne de n occurrences du caractère c. |
string(const string& ss) |
Constructeur de copie. |
string(const string& ss, int debut, int nb) |
Copie une sous-partie à partir de l'indice debut sur nb caractères. |
Exemple de code illustrant les constructeurs :
#include <iostream>
#include <string>
using namespace std;
void demonstration_construction() {
string vide; // Chaîne vide
string copie(vide); // Copie (ou string copie = vide;)
string param("bonjour"); // Constructeur avec chaîne C
string repetee(4, 'z'); // Chaîne avec 4 'z'
string sous_chaine(param, 2, 3); // Extrait 3 caractères à partir de l'indice 2
cout << "vide : " << vide << endl;
cout << "copie : " << copie << endl;
cout << "param : " << param << endl;
cout << "repetee : " << repetee << endl;
cout << "sous_chaine : " << sous_chaine << endl;
}
Membres courants de la classe string
Pour une chaîne str = "salut" et une sous-chaîne recherche = "al" :
| Fonction | Description |
|---|---|
str.empty() |
Vérifie si la chaîne est vide (retourne vrai ou faux). |
str.size() |
Retourne la longueur effective (sans le caractère nul '\0'). |
str.substr(debut, nb) |
Extrait une sous-chaîne à partir de l'indice debut sur nb caractères. |
str.find(motif) |
Recherche la première occurrence d'un motif et retourne son indice, ou une grande valeur si non trouvé. |
str.insert(pos, texte) |
Insère une chaîne à la position pos. |
str.erase(pos, nb) |
Supprime nb caractères à partir de la position pos. |
Notes techniques :
- La position dans
substretfindsuit la logique des indices (commençant à 0). - Utiliser
sizeof(str)sur un objetstringretourne la taille de la classe, pas la longueur de la chaîne. - Combiner
findetsubstrpermet d'extraire des segments spécifiques, comme dans cet exemple :
void extraction_avancee() {
string texte("Bonjour le monde, je suis Alice. Je viens de France.");
// Recherche d'une séquence entre deux marqueurs
size_t debut = texte.find("Alice");
size_t fin = texte.find(".");
if (debut != string::npos && fin != string::npos) {
string segment = texte.substr(debut, fin - debut + 1);
cout << "Segment extrait : " << segment << endl;
}
}
Pour définir la longueur d'un tableau à la compilation, utiliser vector si la taille dépend de size() :
int longueur = str.size();
vector<char> tableau(longueur); // Alternative dynamique
Surcharge d'opérateurs
| Opérateur | Description |
|---|---|
= |
Affecte une chaîne ou un caractère. |
+ / += |
Concatène des chaînes. |
[] |
Accède au caractère à un indice donné, comme dans un tableau. |
Algorithmes de recherche de motif
Dans la correspondance de motifs, la chaîne principale est la cible, et la sous-chaîne est le motif.
Algorithme naïf (BF - Brute Force)
L'algorithme BF parcourt la cible caractère par caractère pour trouver une correspondance exacte avec le motif.
Exemple d'implémentation en C++ :
#include <iostream>
#include <string>
using namespace std;
void recherche_BF() {
string cible, motif;
cout << "Entrez la cible et le motif : ";
cin >> cible >> motif;
int i = 0; // Indice pour la cible
int correspondances = 0;
while (i <= cible.size() - motif.size()) {
int j = 0;
int temp = i;
int compteur = 0;
// Vérification caractère par caractère
while (temp < cible.size() && j < motif.size() && cible[temp] == motif[j]) {
compteur++;
temp++;
j++;
}
if (compteur == motif.size()) {
correspondances++;
cout << "Motif trouvé à l'indice " << i << endl;
break; // Arrêter à la première occurrence
}
i++;
}
if (correspondances == 0) {
cout << "Motif non trouvé." << endl;
}
}
Analyse de complexité :
- Cas défavorable : O((n-m+1)*m), où n est la longueur de la cible et m celle du motif.
- Cas favorable : O(n), si le motif est trouvé immédiatement.
Algorithme de Knuth-Morris-Pratt (KMP)
L'algorithme KMP optimise la recherche en évitant les comparaisons redondantes grâce à un prétraitement du motif, permettant de sauter des positions lors d'une incompatibilité.