188 lines
5.4 KiB
Markdown
188 lines
5.4 KiB
Markdown
|
|
# Corrigé des exercices — Recherche textuelle
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. QCM
|
|||
|
|
|
|||
|
|
| Question | Réponse | Explication |
|
|||
|
|
|----------|---------|-------------|
|
|||
|
|
| 1 | **b.** | On parcourt au maximum tous les caractères du texte une fois : O(n). |
|
|||
|
|
| 2 | **c.** | On teste les positions 0, 1, ..., n-m, soit n - m + 1 positions. |
|
|||
|
|
| 3 | **b.** | Boyer-Moore compare de droite à gauche (fin du motif vers le début). |
|
|||
|
|
| 4 | **b.** | Le pré-traitement permet de savoir de combien décaler après un échec. |
|
|||
|
|
| 5 | **c.** | Dans le pire cas, on compare m caractères pour chacune des n-m+1 positions. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Questions de cours
|
|||
|
|
|
|||
|
|
### Question 1
|
|||
|
|
|
|||
|
|
Texte : `"abracadabra"` (longueur 11), Motif : `"abra"` (longueur 4)
|
|||
|
|
|
|||
|
|
**a.** Nombre de positions testées : n - m + 1 = 11 - 4 + 1 = **8 positions** (indices 0 à 7)
|
|||
|
|
|
|||
|
|
**b.** Le motif `"abra"` est trouvé aux positions :
|
|||
|
|
- **Position 0** : `abra`cadabra
|
|||
|
|
- **Position 7** : abracad`abra`
|
|||
|
|
|
|||
|
|
### Question 2
|
|||
|
|
|
|||
|
|
L'algorithme de Boyer-Moore est plus efficace car :
|
|||
|
|
|
|||
|
|
1. **Comparaison de droite à gauche** : En cas d'échec sur un caractère qui n'existe pas dans le motif, on peut sauter tout le motif d'un coup.
|
|||
|
|
|
|||
|
|
2. **Sauts importants** : Plus le motif est long, plus les sauts potentiels sont grands. Par exemple, avec un motif de 10 caractères et un caractère non présent dans le motif, on peut avancer de 10 positions.
|
|||
|
|
|
|||
|
|
3. **Cas favorable** : Si l'alphabet est grand et le motif long, Boyer-Moore peut avoir une complexité sous-linéaire O(n/m) dans le meilleur cas.
|
|||
|
|
|
|||
|
|
**Exemple** : Rechercher `"ALGORITHME"` dans un texte. Si on tombe sur un `"Z"` (absent du motif), on saute directement de 10 caractères.
|
|||
|
|
|
|||
|
|
### Question 3
|
|||
|
|
|
|||
|
|
**a.** `recherche("bonjour", "jour")` renvoie **3**
|
|||
|
|
|
|||
|
|
Vérification : `bonjour` → à l'indice 3, on trouve `jour`.
|
|||
|
|
|
|||
|
|
**b.** `recherche("bonjour", "soir")` renvoie **-1**
|
|||
|
|
|
|||
|
|
Le motif `"soir"` n'est pas présent dans `"bonjour"`.
|
|||
|
|
|
|||
|
|
**c.** Modification pour renvoyer toutes les positions :
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def recherche_toutes(texte, motif):
|
|||
|
|
n = len(texte)
|
|||
|
|
m = len(motif)
|
|||
|
|
positions = []
|
|||
|
|
for j in range(n - m + 1):
|
|||
|
|
i = 0
|
|||
|
|
while i < m and texte[j + i] == motif[i]:
|
|||
|
|
i = i + 1
|
|||
|
|
if i == m:
|
|||
|
|
positions.append(j)
|
|||
|
|
return positions
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
>>> recherche_toutes("abracadabra", "abra")
|
|||
|
|
[0, 7]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Exercices de programmation
|
|||
|
|
|
|||
|
|
### Exercice 1 : Compter les occurrences
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def compter_occurrences(texte, motif):
|
|||
|
|
"""Compte le nombre d'occurrences du motif dans le texte."""
|
|||
|
|
n = len(texte)
|
|||
|
|
m = len(motif)
|
|||
|
|
compteur = 0
|
|||
|
|
for j in range(n - m + 1):
|
|||
|
|
i = 0
|
|||
|
|
while i < m and texte[j + i] == motif[i]:
|
|||
|
|
i = i + 1
|
|||
|
|
if i == m:
|
|||
|
|
compteur += 1
|
|||
|
|
return compteur
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Tests** :
|
|||
|
|
```python
|
|||
|
|
>>> compter_occurrences("abracadabra", "a")
|
|||
|
|
5
|
|||
|
|
>>> compter_occurrences("abracadabra", "abra")
|
|||
|
|
2
|
|||
|
|
>>> compter_occurrences("aaaa", "aa")
|
|||
|
|
3 # Occurrences aux positions 0, 1 et 2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Exercice 2 : Recherche insensible à la casse
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def recherche_insensible(texte, motif):
|
|||
|
|
"""Recherche un motif sans tenir compte des majuscules/minuscules."""
|
|||
|
|
texte_min = texte.lower()
|
|||
|
|
motif_min = motif.lower()
|
|||
|
|
n = len(texte_min)
|
|||
|
|
m = len(motif_min)
|
|||
|
|
for j in range(n - m + 1):
|
|||
|
|
i = 0
|
|||
|
|
while i < m and texte_min[j + i] == motif_min[i]:
|
|||
|
|
i = i + 1
|
|||
|
|
if i == m:
|
|||
|
|
return j
|
|||
|
|
return -1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Tests** :
|
|||
|
|
```python
|
|||
|
|
>>> recherche_insensible("Bonjour tout le Monde", "monde")
|
|||
|
|
16
|
|||
|
|
>>> recherche_insensible("PYTHON", "python")
|
|||
|
|
0
|
|||
|
|
>>> recherche_insensible("Hello World", "WORLD")
|
|||
|
|
6
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Exercice 3 : Analyse de complexité
|
|||
|
|
|
|||
|
|
Texte : `"aaaaaaaaaa"` (10 'a'), Motif : `"aaab"` (3 'a' puis 'b')
|
|||
|
|
|
|||
|
|
**a.** Calcul du nombre de comparaisons :
|
|||
|
|
|
|||
|
|
- Positions testées : 10 - 4 + 1 = 7 positions (indices 0 à 6)
|
|||
|
|
- À chaque position, on compare les 3 premiers 'a' avec succès, puis échec sur 'b'
|
|||
|
|
- Soit 4 comparaisons par position
|
|||
|
|
|
|||
|
|
**Total : 7 × 4 = 28 comparaisons**
|
|||
|
|
|
|||
|
|
**b.** Ce cas est défavorable car :
|
|||
|
|
|
|||
|
|
- Le motif partage un long préfixe avec le texte (`"aaa"`)
|
|||
|
|
- À chaque tentative, on effectue presque toutes les comparaisons avant l'échec
|
|||
|
|
- On ne décale que d'une position à chaque fois
|
|||
|
|
- C'est le pire cas : beaucoup de comparaisons pour peu d'avancement
|
|||
|
|
|
|||
|
|
Ce type de motif (caractères répétés suivis d'un caractère différent) maximise le nombre de comparaisons.
|
|||
|
|
|
|||
|
|
### Exercice 4 : Table des dernières occurrences
|
|||
|
|
|
|||
|
|
Pour le motif `"EXEMPLE"` (indices 0 à 6) :
|
|||
|
|
|
|||
|
|
| Caractère | Dernière position |
|
|||
|
|
|-----------|-------------------|
|
|||
|
|
| E | 6 |
|
|||
|
|
| X | 1 |
|
|||
|
|
| M | 3 |
|
|||
|
|
| P | 4 |
|
|||
|
|
| L | 5 |
|
|||
|
|
|
|||
|
|
**Note** : Le caractère 'E' apparaît aux positions 0 et 6. On garde la dernière occurrence (6).
|
|||
|
|
|
|||
|
|
**Construction en Python** :
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def table_occurrences(motif):
|
|||
|
|
table = {}
|
|||
|
|
for i in range(len(motif)):
|
|||
|
|
table[motif[i]] = i
|
|||
|
|
return table
|
|||
|
|
|
|||
|
|
>>> table_occurrences("EXEMPLE")
|
|||
|
|
{'E': 6, 'X': 1, 'M': 3, 'P': 4, 'L': 5}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Utilisation** : Si on rencontre un caractère 'X' lors d'un échec, on sait que la dernière occurrence de 'X' dans le motif est à la position 1. On peut donc calculer le décalage optimal.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
Auteur : Florian Mathieu
|
|||
|
|
|
|||
|
|
Licence CC BY NC
|
|||
|
|
|
|||
|
|
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|