Recherche textuelle

Source : Gilles Lassus
I. La méthode find de Python⚓︎
Auteurs : Marine Méra - Modification par François Hallé, Jean-Louis Thirot et Mireille Coilhac
À vous de jouer 1 : trouver une lettre dans un mot
Écrire une fonction trouve_lettre qui prend en paramètres un caractère cet une chaîne de caractères texte et qui renvoie la **première** occurrence decdanstexte`.
La fonction devra renvoyer None si c est absent de texte.
Contraintes
On n'utilisera pas ni la fonction index, ni la fonction find.
Plus difficile
Le problème est plus difficile quand il faut chercher non plus un seul caractère mais un mot dans le texte.
- on ne parlera pas de 'mot' mais de motif, ce qui est plus général.
- quand on trouve le motif cherché à un endroit du texte, on dira qu'il s'agit d'une occurrence du motif dans le texte : cela désignera l'indice
itel quetexte[i:i+1] == motif
Rappel : la notation chaine[i:j] désigne la tranche de la chaîne comprise entre i inclus et j exclu. On parle de slicing.

Exécuter le code ci-dessous
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
La fonction find de Python
Python dispose d'une méthode find attachée aux objets chaînes de caractère qui permet justement de trouver un motif dans la chaîne
Pour corser un peu l'affaire, on peut prendre un texte très long. Typiquement, on peut chercher un mot ou une phrase dans tout le texte d’un roman.
Le site http://www.gutenberg.org/browse/languages/fr propose les grands classiques de la littérature qui sont tombés dans le domaine public. On peut par exemple y trouver le texte intégral du roman Le rouge et le noir de Stendhal dans l’encodage UTF-8 : http://www.gutenberg.org/ebooks/798.txt.utf-8
À vous de jouer 2
Nous allons ouvrir ce fichier texte avec Python et charger l'intégralité du fichier texte dans une variable nommée stendhal. Exécuter ci-dessous
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
On peut chercher ensuite si le motif "Julien trembla" apparaît quelque part dans le roman.
- La méthode
findrenvoie un entier correspondant à l'indice de la première occurrence du motif dans le texte. - La méthode
findrenvoie -1 si le motif cherché n'apparaît pas dans le texte. Par exemplestendhal.find('Joséphine')renvoie −1 car le prénom Joséphine n’apparaît jamais dans le roman.
Exécuter ci-dessous
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
À vous de jouer 3
Une variante de la méthode find a deux arguments. Le deuxième est un entier égal à l'indice de départ de la recherche.
Question 1
En utilisant ce deuxième argument, trouvez s'il y a une occurrence suivante du motif : 'Julien trembla'
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Question 2
Trouver les deux premières occurrences du mot : 'Julien' dans le roman
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
À vous de jouer 4
Question 1
La fonction nb_occurrences prend en paramètres deux chaines de caractères texte et motif.
Elle renvoie le nombre de fois où motif apparaît dans texte.
Compléter ci-dessous.
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Question 2
Reprendre la fonction de la question précédente avec une fonction récursive.
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
II. Recherche textuelle naïve⚓︎
Regarder les 5 premières minutes de la vidéo de l'introduction.
L'algorithme naïf et l'algorithme de Horspool en vidéo : Recherche textuelle
Illustration de l'algorithme
Auteur : Gilles Lassus

Animation de Nicolas Revéret
Algorithme de recherche naïve
Compléter ci-dessous :
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Version booléenne de l'algorithme de recherche naïve
Re-écrire l'algorithme précédent en s'arrêtant dès qu'une occurrence de motif est trouvée dans texte.
La fonction renverra uniquement un booléen.
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Temps de recherches par différentes méthodes
Compléter ci-dessous la fonction naive_find qui code la fonction find de Python de façon naïve
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Nous allons mesurer les temps d'exécution de ces deux fonctions pour des recherches dans le même texte que précédemment, "Le rouge et le noir". La fonction naive_find est en code caché.
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Comparons le temps d'exécution en fonction de la longueur du motif cherché avec la recherche naïve
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Comparons le temps d'exécution en fonction de la longueur du motif cherché avec la recherche find
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
😭 Et dans le pire des cas ?
Construisons un texte (très long ! un million de caractères - tous identiques - par exemple) et un motif (assez long lui aussi ! Mettons mille caractères - les mêmes plus un caractère différent à la fin) correspondant au pire des cas, et comparons les deux temps de calcul.
Vous verrez c'est assez long...
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
III Le principe de l'algorithme Boyer Moore Horspool⚓︎
En bio-informatique
Les algorithmes de recherche textuelle sont aussi notamment utilisés en bio-informatique. C’est dans ce domaine que l’on va prendre les exemples du TP qui suivra.
- Comme son nom l'indique, la bio-informatique est issue de la rencontre de l'informatique et de la biologie : la récolte des données en biologie a connu une très forte augmentation ces 30 dernières années. Pour analyser cette grande quantité de données de manière efficace, les scientifiques ont de plus en plus recourt au traitement automatique de l'information, c'est-à-dire à l'informatique.
- Analyse de l'ADN : Comme vous le savez déjà, l'information génétique présente dans nos cellules est portée par les molécules d'ADN. Les molécules d'ADN sont, entre autres, composées de bases azotées ayant pour noms : Adénine (représenté par un A), Thymine (représenté par un T), Guanine (représenté par un G) et Cytosine (représenté par un C).

Auteur du schéma : Victoria Denys/CEA sur https://www.cea.fr/comprendre/Pages/sante-sciences-du-vivant/l-ADN-dechiffrer-pour-mieux-comprendre-le-vivant.aspx?Type=Chapitre&numero=1
Algorithme de recherche naïve en partant à l'envers
Auteur : Gilles Lassus

Re-écrire l'algorithme de recherche naïve, mais en démarrant de la fin du motif et non du début. Certes, c'est pour l'instant très artificiel, mais cela va nous aider 😊.
Compléter ci-dessous :
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Le principe
L'idée est d'améliorer le code précédent (celui où l'on parcourt le motif à l'envers) en sautant directement au prochain endroit potentiellement valide.
Pour cela on regarde le caractère X du texte sur lequel on s'est arrêté (car X n'était pas égal au caractère de rang équivalent dans le motif):
Si X n'est pas dans le motif, il est inutile de se déplacer "de 1" : on retomberait tout de suite sur X, c'est du temps perdu. On se décale donc juste assez pour dépasser X, donc de la longueur du motif cherché. Si X est dans le motif (sauf à la dernière place du motif!), on va regarder la place de la dernière occurence de X dans le motif. On se décale afin de faire coïncider le X du motif et le X du texte.
Visualisation Boyer Moore Horspool par Nicolas Revéret
Faire plusieurs essais en modifiant le texte à parcourir et le motif à chercher.
IV. Implémentation de l'algorithme Boyer Moore Horspool⚓︎
Utiliser un prétraitement du motif pour déterminer les décalages
On va d'abord coder une fonction dico_lettres qui renvoie un dictionnaire associant à chaque lettre de mot sauf la dernière son dernier rang dans la variable mot.
Dans l'exemple suivant :

Le dictionnaire créé sera donc : dico = {"s": 0, "t": 1, "r": 2, "i": 3, "n": 4}
Comprendre les variables utilisées
Nous utilisons les mêmes variables i et k que dans l'algorithme de recherche naïve en partant à l'envers vu précédemment.
Au début, pour la recherche du motif "attg" dans le texte "atttcgattgc" nous avons la situation suivante :
La recherche démarrera donc avec i = 0 et k = len(motif) - 1
- La variable
isert à se déplacer vers la droite surtexteen partant du début detexte. - La variable
ksert à se déplacer vers la gauche surmotifen partant de la fin demotif.
Lorsque l'on a positionné le motif sous le texte, i correspond donc à l'indice de la première lettre de
texte sous laquelle se trouve la première lettre de motif
À vous de jouer
Dans la situation suivante :

1. Donner le dictionnaire dico de prétraitement du motif
Solution
dico = {"g": 4, "a": 1, "t": 3}
Ne pas oublier que pour chaque caractère on donne le rang de la dernière occurence, en excluant le dernier caractère.
2. Dans le déroulement de l'algorithme, on est positionné sur les cases rouges. Donner i et k
Solution
i = 1 et k = 3
3. Compléter en utilisant les noms de variables i et k :
4. Quelle est la plus grande valeur que peut prendre i en fonction de len(texte) et len(motif) ?
Solution
len(texte) - len(motif)
Par exemple dans l'exemple ci-dessous la plus grande valeur possible de i vaut 9.
En effet : len(texte) - len(motif) = 13 - 4 = 9

5. Compléter pour le "a" en vert du texte : texte[i + ... ] = "a" dans la situation suivante :

Donner la réponse en fonction de len(motif)
Solution
texte[i + len(motif) - 1 ] = "a"
Comprendre les décalages à réaliser
1. Décalage pour réaliser un alignement

Le dictionnaire de prétraitement du motif est le suivant : dico = {"c": 3, "g": 2, "a": 4}
Il faut aligner les deux caractères "c", et pour cela faire un décalage pour i de 5 - 3
On obtiendra donc :

Par quel calcul trouve-t-on qu'il faut faire un décalage de 2 ? L'exprimer avec les noms de variables uilisés.
Aide
5 - dico["c"]
- Ecrire 5 en fonction de
len(motif) - Remplacer
"c"pardico[text[...]]
Solution
(len(motif) - 1) - dico[text[i + (n - 1)]] = 5 - 3 = 2
2. Comprendre le dictionnaire de prétraitement
Quel aurait été le problème si le dictionnaire de prétraitement avait contenu la dernière occurence du dernier caractère du motif?
Solution
Le dictionnaire de prétraitement du motif aurait été le suivant : dico = {"c": 5, "g": 2, "a": 4}
En utilisant la formule précédante on aurait obtenu un décalage de 5 - 5 = 0 !
👉 On comprend donc pourquoi on exclut le dernier caractère su motif du dictionnaire.
3. Cas où on ne peut pas réaliser d'alignement

Le dictionnaire de prétraitement du motif est le suivant : dico = {"g": 4, "t": 3}
"a"n'est pas dans dico. On peut donc directement faire un grand saut de longueur len(motif) . Cela correspond à un décalage de 6 . i prendra la valeur 1 + 6 = 7
On obtiendra :

Le principe de l'algorithme de Boyer Moore Horspool
- Il faut avoir réalisé le dictionnaire de prétraitement du motif
- On fait varier
ide 0 jusqu'à la dernière valeur possible. - Pour chaque
i, on observe les correspondances entre les lettres du texte et celles du motif, en partant de la fin. - Si toutes les lettres correspondent, on a trouvé un indice qui répondait au problème. On incrémente
ide 1 pour faire une nouvelle recherche. - Sinon, on se replace au caractère du texte superposé au dernier caractère du motif. Suivant le cas :
- soit on réalise un décalage approprié pour aligner les caractères
- soit on effectue un saut de la taille du motif.
À vous de jouer : Implémenter l'algorithme Boyer Moore Horspool
Compléter ci-dessous :
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
Algorithme de Boyer-Moore-Horspool 
def BMH(texte, motif):
dico = dico_lettres(motif)
n = len(motif)
indices = [] # La liste des indices auxquels se trouvent le motif cherché
i = 0
while i <= len(texte) - n: #(1)
k = n - 1
while k >= 0 and texte[i + k] == motif[k]: # Tant qu'il y a correspondance
k = k - 1
if k == -1: #(2)
indices.append(i)
i = i + 1 #(3)
else: #(4)
if texte[i + n - 1] in dico: #(5)
i = i + n - 1 - dico[texte[i + n - 1]]
else: #(6)
i = i + n
return indices
- On remonte le motif à l'envers, tant qu'il y a correspondance et qu'on n'est pas arrivé au début du motif
- Si on est arrivé à la valeur
k = -1, c'est qu'on a parcouru tout le mot : on l'a donc trouvé. - On a trouvé le motif, mais attention, il ne faut pas trop se décaler sinon on pourrait rater d'autres occurences du motif (pensez à la recherche du motif «mama» dans le mot «mamamamama»). On se décale donc de 1.
- On s'est arrêté avant la fin, sur une lettre présente dans le mot : il va falloir faire un décalage intelligent.
- On décale juste de ce qu'il faut pour mettre en correspondance la lettre de texte positionnée au dessus de la dernière de motif, avec la dernière occurence de cette lettre dans motif.
- On fait un saut de la logueur du motif.
V. QCM⚓︎
L'algorithme de Boyer-Moore
Dans tout ce QCM on considère la fonction BMH qui prend en paramètres deux chaînes de caractères texte et motif, et qui renvoie la liste des indices où se trouve motif dans texte. La taille de motif est n
-
dico_lettresest la fonction qui fait le prétraitement du motif cherché.Que renvoie
dico_lettres("pacecap")?-
{"p": 0, "a": 1, "c": 2, "e": 3, "c": 4, "a": 5, "p": 6} -
{"p": 0, "a": 1, "c": 2, "e": 3} -
{"p": 0, "a": 5, "c": 4, "e": 3} -
{"p": 6, "a": 5, "c": 2, "e": 3}
Remarque
.8594o4l5s_L06.:/phPnA1gk2cj=iè)àftîear{3},(d véumbqE050n040h0H0P0D0b0q0w0E0z0(0P050w0b0O0G040O0z0:0g0d0G0E0E0I0G0f050m0:0=040P0H0f0f0b0w0z0G0P0C0P0w0o0H0V0S1f0|0~100P0f0H0S0D0P0d0#0=0I0q0z0A100M1s0-050f0 0(0t0@0G1B1e0I0P0I0H0q0t131K0I1M170O1V0f1x1f0T0b0+0D0k060p0b0S1S0/0;0?010n0H0w0G0w0H0n0113150?0P0N0z0q0^1 1)0i1g0P0j0B0P0l130Y06050S0d042o0d0z041_161|24141`17221}1U0F0E0P0G0q0P0n0b0f0z0+0(2e2J2I0j0P0a0P0-100+2K2I1J1L1W040j1Y2*1N132u2s052=2x1{0H2A262D0n2F0H2H2J2L2N2P2R0q1)0s2U0P0e2X2Z2L2#1e0q2(1Z1#0e2.1!2+2;2v2t2v2_04010w2|2C182 0H2G2I2K2M2O2Q0,380P0v3b0c3e2!0}3i3k2/040c3o1#3r2?2^2}010G3z163B30320S1C1m0G0T2%333H363K0P0K3Q3g3S3@2)3p1N0K3Y3q0m2=2o0m2q2?0Y0r2a2P2k0.2}2c0b0P0y0P0J2z0l2h1G012{4s0e4u3y4s0c4u3)4s0K0L252C2m2?0U0d1c0u1m1-0?2o0Y421#0p1D0t1f2l0m3l2+0P0W2L211)0=1*1q1f0I0R0n0R0E0R0G1G0w00112I0E1?0x1?0I1)1y0.4%1N1A1C1E1f1c0w1@100)0G461N0P1m0z2J1K1i2S1P0Q4`0k4K4a4M4O4Q0E0?. -
-
Au début de la fonction
BMH, avec quelles valeursietksont-ils initialisés ?-
i = 1etk = 0 -
i = 0etk = 0 -
i = 0etk = len(motif) - 1 -
i = len(motif) - 1etk = 0
Remarque
.olsx0.ê/phngkciè)àtfear,(d éumb050i04050n0a0z0u040o050h0J0L040A0i0a0o0k0s0u0A0c0C0w0A0b0!0z0B0E0C0s0A0L0)0v0A0t0u0k0g0s0w0+0v0k0c0)0!0Z0d0Z0A0y0,0D0v0w0~0A0r0A0e0q0x0A0u0:0I0K0M0m0P0R0M0U0W0Y0!0$0(0*0A050c0}0a0k0l040L0w0k0o0u0(0n1c0v0n0s0p1e0z0C0A0D0a0s0o0t0P1E0w1G1I1v0%0A0J0D0D0`0n1O0?1:0a0D0i1R0o0c1G0;0+1+0o171g0l0v0C0n0j0u0f0P0G. -
-
Dans la boucle interne
while k >= 0 and texte[i + k] == motif[k], que se passe-t-il si on sort de cette boucle aveck == -1?- On a trouvé une non-correspondance : on effectue un décalage.
-
Toutes les lettres ont correspondu : on ajoute
ià la liste des résultats, puis on incrémenteide 1. - On place le motif juste après le caractère différent.
- On arrête complètement la recherche.
Remarque
.ols./phn1gkc=i)-ftàear,(dO véumq050f04050l0a0y0t040k0A0m0m0A0p0i050e0K0M040A0c0n0j0h0n0q0n0t0A0F0D0-0r0a0D0r0t0c0A0b0_0A0K0E0f0u0v0u0n0c0a0h0`0x0M0A0y0v0a0n0^0A0s0A0j0u0D0l0g0t0o0A170r0A0v0C0D0c0$0d0A0z0h0A0t0h0v0t0j150r1I0{0u0A0f0a0$0r0n170A0J0L0N0n0W0Y0N0w1Q0D151s1E0u0B0u0h0l0-1Y0Z1#0X1Z0!1b0i1Q0?0v0~1p0v1o0t220D0h0-0C0B1G0r0:0b0|0A1m1M0-0a0l0l0D0v1I1=0t0d0W0H. -
Dans le bloc
else(non-correspondance), lorsquetexte[i + n - 1]est présent dansdicoquelle est la nouvelle valeur dei?-
i = i + n - 1 - dico[texte[i + n - 1]] -
i = i + 1 -
i = i + k + 1 -
i = i + len(motif)
Remarque
.ols/pngcCiteard éum050e040i000l0c0k0p0b0l0p0o0q0h0m0b0m0g0D0e0a0r0n0p0n0q0I0j0c0l0Q0b000U0g0f0l0s0l0f0k050d0u. -
-
Dans le bloc
else(non-correspondance), lorsque le caractèretexte[i + n - 1]n'est pas dansdico, quelle est la nouvelle valeur deiet pourquoi ?-
i = i + k -
i = i + n -
i = i + n - 1 -
i = i + dico[motif[-1]]
Remarque
.olsx./+pngc=iftear,d um050h04050k0a0t0p040m0u0l0u0G0g0u0i050f0B0D040s0u0k0q0r0u0a0i0u0n0q0m0o0u0b0p0u0c0q0v0)0w0q0d0m0=0b0u0D0*0Z0j0v0p0v0X0A0C0E0N0P15040e0O0y. -
-
Dans quel sens l'algorithme de Boyer-Moore-Horspool compare-t-il les caractères du motif avec ceux du texte ?
- De gauche à droite, comme l'algorithme naïf.
- De droite à gauche au sein de la fenêtre courante.
- En commençant par le milieu du motif.
- Dans un ordre aléatoire déterminé par le dictionnaire de prétraitement.
Remarque
.olsxL./phngcCiètfera,d véumbq050h040e000t0b0k0a0s0n0p0i0A0r0w0l0a0A0Q0j0l0R0h0t0s0S0U0!0s0r0$0b0R0v0+0j0n0+0S0#0t0l0p0o0*0w0v0z0w0A0a0p0n0q0w0t0x0r0l0w0-0@0s0_0{0}0T0s0*0c0h0a0j0v0t0j0p0~1r0c1c0R0p0r0d1z0u0w0h0z0n1w0*121s0R190s1w1d0v0y0B0z1t0 1113150f0w0m000r0c1t0Y0w0c0r0j1w0/0%0A0)0t1H1o0w0C1G1E0+0Q1V1%0d0h0b0a0N0?0r0q0q0n0l0_0r0W1t0-1:0y2d0b0t0k1(0f050g0E. -
À quoi sert le dictionnaire de prétraitement
dicoconstruit à partir du motif ?- À stocker toutes les positions du motif dans le texte.
- À compter le nombre d'occurrences de chaque lettre dans le texte.
- À connaître, pour chaque lettre du motif sauf la dernière, son dernier rang, afin de calculer le décalage à effectuer en cas de non-correspondance.
- À trier les caractères du motif par ordre alphabétique.
Remarque
.oQlsx./phngcièàtfear,d véumb050h040b0z0s0j0v0w0z0j0r0w0c0r0p0p0t0N0v0z0w0p0r0e0Y0w0M0w0l0a0t0T0d0h0a0I0w0h0s0d0w0s0W0l0s0t0s0l0p0n0T0^0R0r0I0W0V0w0A0a0p0m0q0u0w0/0(0/0d0z0c0#050)0v0r040v0m0)050g1p1r0;0a0z0t0w0d0s0x0a0m1D1q1i0A0B0m151g0j0;0r0z0p0w0v0y0{0P1D0P191b1d0w0x0r0t0@0c0s1X0t1I0Y1f1Q0l0i1-1{0H1W0o0^0c0m0k0M1D0l0Q0#0P0R121G0r0l1E1;1q0t0j0m110N0a0l0l1C0T0j281X0H1/0N1a1c0q0f1w0D. -
Quelle est la valeur maximale que peut prendre
i(l'indice de début de fenêtre) pendant la recherche du motif dans le texte ?-
len(texte) - 1 -
len(texte) -
len(texte) - len(motif) -
len(texte) + len(motif)
Remarque
.olsxL.ê/pnAgciè)-àtfear,(d vé;umbq050i040k0E0q0z0u0b0r0A0O0A0m0u0s0s0u0A0B0v0b0u0E0w0x0A0%0A0F0a0s0n0t0S0C0G0a0w0O0w0v0n0s0S0E0A0Y0d0Y0A0W0,0v0U0a0F0i0v0}0n0c0a0j0A0j000v0)0~100i0b0E0c0S0Z0c0u0j0c0f0A0e1a0_0E0m0-0i0w0n0j0m0n1e0-0c1n0w0w0g160z1j0m0S0o1v0H0E0Z050m0a0O040n0A020l0s0D0,1z0y140Y0p0A0q1_0j0y0/0;0t0p050h1,1.0f280J. -
-
Parmi les affirmations suivantes sur l'algorithme de Boyer-Moore-Horspool, laquelle est correcte ?
- L'algorithme ne peut trouver qu'une seule occurrence du motif dans le texte.
- L'algorithme repart toujours du début du texte après chaque comparaison.
- L'algorithme peut sauter plusieurs caractères du texte sans les examiner, ce qui le rend potentiellement plus rapide que l'algorithme naïf.
- L'algorithme nécessite que le motif soit trié par ordre alphabétique pour fonctionner.
Remarque
.oBlïsx.M/pHhngcCjGièàt-fera,d véuâmyq050j040r0z0H0o0y0D0A0G0f0D0C0F0o0A0c0A0n0y0e0D0!0c0o0G0c0F0*0u0D0j0A0z0v0s0z0X0G0X0s0o0{0a0m0m0A0|0S0C0S0j0z0F0v0z170v0y0I0y0m0v0B0D0b0a0J0y0z0w0h0a0a0z0y0w0k1y0e0j1x0c0@0y0G0v0D0s0n0m1y1t0X0S0n1g0m1a0*1F0`0s140*0C0 1i0f1i0g0D0p000)1L0e140@0z0s0m0o0s0^1H0A0E0A1m0%0S0e0G0}0$0D1z0o0l1t2c0S160d0E0y1o0K0G0s0T211{0S0v0a0G0q2u0z1%000G0m0D0e1J1H0!1g120t1z0D0?290x0a0s0e0g050i0M.
VI. Crédits⚓︎
Gilles LASSUS, Nicolas REVERET, Jean-Louis THIROT, Marine MERA et Mireille COILHAC
# Tests(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)