<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bogdan</id>
	<title>Wiki du LAMA (UMR 5127) - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bogdan"/>
	<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php/Sp%C3%A9cial:Contributions/Bogdan"/>
	<updated>2026-06-10T03:03:42Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15969</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15969"/>
		<updated>2025-05-16T14:58:04Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Tableau de suffixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_array_bananas.png||thumb|right|Tableau de suffixes de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes est une structure légère qui représente indirectement tous les suffixes d&#039;une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
Il stocke chaque indice de début de suffixe dans l&#039;ordre alphabétique des suffixes.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour le créer, il suffit de créer un tableau contenant tous les indices de la chaîne d&#039;origine et de trier la liste suivant les suffixes commençant aux l&#039;indices.&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans le tableau de suffixes, il suffit d&#039;utiliser la recherche dichotomique, ce qui donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes présente comme avantages : sa taille (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine) et sa rapidité de recherche (&amp;lt;math&amp;gt;mlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern et &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine.&lt;br /&gt;
La ta construction du tableau de suffixes peut cependant être longue lorsque la chaîne est très répétitive (exemple : que des &amp;quot;a&amp;quot;) ce qui augment fortement la complexité (&amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine).&lt;br /&gt;
&lt;br /&gt;
Cependant bien que la construction et la recherche soient plus longues dans de très longues chaînes de caractères avec le tableau de suffixe qu&#039;avec le Trie ou l&#039;arbre de suffixe, la taille de la structure est un aspect non-négligeable pour la recherche de patern.&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_transformee_bw_bananas.png||thumb|right|Transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
La transformée de Burrows-Wheeler permet de transformer une chaîne de caractère en une autre ayant pour propriété que les caractères semblables éloignés dans la chaîne d&#039;origine se retrouvent plus fréquemment collés (par exemple, la transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot; est &amp;quot;sbnn$aaa&amp;quot;). Cette propriété permet de faciliter la compression de la chaîne grâce aux répétitions de caractères.&lt;br /&gt;
&lt;br /&gt;
Pour faire la transformée d&#039;une chaîne, il faut prendre chaque suffixe de la chaîne d&#039;origine, recommencer à la fin de ceux-ci la chaîne jusqu&#039;à ce qu&#039;il y ait le même nombre de caractère dans la chaîne d&#039;origine et dans celle-ci, trier les chaînes dans l&#039;ordre alphabétique, récupérer la dernière lettre de chaque chaîne dans l&#039;ordre.&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de générer la transformée de Burrows-Wheeler :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EOS = chr(28)&lt;br /&gt;
def convertion_bw(mot: str) -&amp;gt; str:&lt;br /&gt;
    mot = mot + EOS&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    tab = [mot[i:] + mot[:i] for i in range(taille_mot)]&lt;br /&gt;
    tab.sort()&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([ligne[taille_mot - 1] for ligne in tab])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La transformée est réversible en connaissant le dernier caractère de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Pour se faire, il faut suivre l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
bw &amp;lt;- transformée de Burrows-Wheeler&lt;br /&gt;
triee &amp;lt;- bw trié dans l&#039;ordre alphabétique&lt;br /&gt;
i &amp;lt;- indice du caractère de fin de chaîne dans la transformée&lt;br /&gt;
mot_origine &amp;lt;- &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Tant triee[i] différent de caractère de fin de chaîne&lt;br /&gt;
    mot_origine &amp;lt;- mot_origine + triee[i]&lt;br /&gt;
    i &amp;lt;- indice de la lettre dans la transformée de Burrows-Wheeler (si 2eme &amp;quot;a&amp;quot; dans triee, alors prendre l&#039;indice du 2eme &amp;quot;a&amp;quot; dans bw)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cet algorithme ne garde pas le cactère de fin de chaîne&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de retrouver la chaîne d&#039;origine à partir de la transformée (garde le caractère de fin de chaîne) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def deconvertion_bw(bw: str) -&amp;gt; str:&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1]) &lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(len(bw)):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return mot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il est donc possible de transformer une chaîne de caractères en une autre qui peut être facilement compressée et de faire le chemin inverse.&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
Il est possible de passer du tableau de suffixes à la transformée de Burrows-Wheeler et inversement car les deux commencent de la même manière : prendre les suffixes d&#039;une chaîne et les trier dans l&#039;ordre alphabétique. Le tableau de suffixes stocke ensuite l&#039;information sous la forme d&#039;un tableau d&#039;indices tandis que la transformée de Burrows-Wheeler sous la forme d&#039;une chaîne de caractère.&lt;br /&gt;
&lt;br /&gt;
La passage de tableau de suffixes à transformée de Burrows-Wheeler est simple. Il suffit de prendre la lettre à l&#039;indice précédant le début du suffixe dans l&#039;ordre du tableau de suffixe (si l&#039;indice du suffixe est 0, alors il faut prendre la dernière lettre de la chaîne).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def sa_en_bw(suffix_array: tuple) -&amp;gt; str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le suffix array en transformé de Burrows Wheeler&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mot = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([mot[(i-1) % taille_mot] for i in array])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour retrouver le tableau de suffixes à l&#039;aide de la transformée de Burrows-Wheeler, il suffit d&#039;appliquer l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sa &amp;lt;- tableau de la taille de la transformée - 1&lt;br /&gt;
bw &amp;lt;- transformée de Burrows-Wheeler&lt;br /&gt;
triee &amp;lt;- bw trié dans l&#039;ordre alphabétique&lt;br /&gt;
i &amp;lt;- indice du caractère de fin de chaîne dans la transformée&lt;br /&gt;
mot_origine &amp;lt;- &amp;quot;&amp;quot;&lt;br /&gt;
j &amp;lt;- 0&lt;br /&gt;
&lt;br /&gt;
Tant triee[i] différent de caractère de fin de chaîne&lt;br /&gt;
    mot_origine &amp;lt;- mot_origine + triee[i]&lt;br /&gt;
    sa[i] = j&lt;br /&gt;
    i &amp;lt;- indice de la lettre dans la transformée de Burrows-Wheeler (si 2eme &amp;quot;a&amp;quot; dans triee, alors prendre l&#039;indice du 2eme &amp;quot;a&amp;quot; dans bw)&lt;br /&gt;
    j &amp;lt;- j + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cet algorithme ne garde pas le caractère de fin de chaîne.&lt;br /&gt;
&lt;br /&gt;
Le passage de la transformée au tableau de suffixes se fait via le code suivant en Python (en gardant le caractère de fin de chaîne) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def bw_en_sa(bw: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le transformé de Burrows Wheeler en suffix array&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille = len(bw)&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1])&lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    sa = [0 for j in range(taille)]&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(taille):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        sa[indice] = i&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return (mot, sa)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On a donc la possibilité de transformer une chaîne de caractère afin de faire qu&#039;elle prenne moins de place et avec la chaîne transformée, générer une structure permettant de faire efficacement une recherche de patern dans une chaîne de caractère.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Comparaison ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_temps_constructions_structures.png|500px|center|thumb|Graphique du temps de génération des structures en fonction de la taille de la chaîne transformée]]&lt;br /&gt;
&lt;br /&gt;
Le graphique ci-dessus nous montre que poure des chaînes de caractères très grandes, il n&#039;est pas avisé d&#039;utiliser un Trie pour la recherche de patern, ni même un arbre de suffixes utilisant le Trie pour se générer. Le choix repose donc entre l&#039;arbre de suffixe (générer sans Trie) et le tableau de suffixe, cependant nous pouvons voir sur le graphique que la tableau de suffixes tend à être plus efficace et rapide à construire que l&#039;arbre de suffixes lorsque la chaîne de caractère est grandissante. Il peut donc être justicieux d&#039;utiliser l&#039;arbre de suffixes plutôt que le tableau de suffixes pour sa rapidité de construction et son stockage plus efficace bien qu&#039;il soit un peu moins efficace pour rechercher un patern.&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15955</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15955"/>
		<updated>2025-05-15T21:30:13Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_array_bananas.png||thumb|right|Tableau de suffixes de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes est une structure légère qui représente indirectement tous les suffixes d&#039;une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
Il stocke chaque indice de début de suffixe dans l&#039;ordre alphabétique des suffixes.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour le créer, il suffit de créer un tableau contenant tous les indices de la chaîne d&#039;origine et de trier la liste suivant les suffixes commençant aux l&#039;indices.&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans le tableau de suffixes, il suffit d&#039;utiliser la recherche dichotomique, ce qui donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes présente comme avantages : sa taille (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine), sa rapidité de construction (&amp;lt;math&amp;gt;nlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine) et sa rapidité de recherche (&amp;lt;math&amp;gt;mlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern et &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Cependant bien que la recherche est plus longue dans de très longues chaînes de caractères avec le tableau de suffixe qu&#039;avec le Trie ou l&#039;arbre de suffixe, la taille de la structure est un aspect non-négligeable pour la recherche de patern.&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_transformee_bw_bananas.png||thumb|right|Transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
La transformée de Burrows-Wheeler permet de transformer une chaîne de caractère en une autre ayant pour propriété que les caractères semblables éloignés dans la chaîne d&#039;origine se retrouvent plus fréquemment collés (par exemple, la transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot; est &amp;quot;sbnn$aaa&amp;quot;). Cette propriété permet de faciliter la compression de la chaîne grâce aux répétitions de caractères.&lt;br /&gt;
&lt;br /&gt;
Pour faire la transformée d&#039;une chaîne, il faut prendre chaque suffixe de la chaîne d&#039;origine, recommencer à la fin de ceux-ci la chaîne jusqu&#039;à ce qu&#039;il y ait le même nombre de caractère dans la chaîne d&#039;origine et dans celle-ci, trier les chaînes dans l&#039;ordre alphabétique, récupérer la dernière lettre de chaque chaîne dans l&#039;ordre.&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de générer la transformée de Burrows-Wheeler :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EOS = chr(28)&lt;br /&gt;
def convertion_bw(mot: str) -&amp;gt; str:&lt;br /&gt;
    mot = mot + EOS&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    tab = [mot[i:] + mot[:i] for i in range(taille_mot)]&lt;br /&gt;
    tab.sort()&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([ligne[taille_mot - 1] for ligne in tab])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La transformée est réversible en connaissant le dernier caractère de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Pour se faire, il faut suivre l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
bw &amp;lt;- transformée de Burrows-Wheeler&lt;br /&gt;
triee &amp;lt;- bw trié dans l&#039;ordre alphabétique&lt;br /&gt;
i &amp;lt;- indice du caractère de fin de chaîne dans la transformée&lt;br /&gt;
mot_origine &amp;lt;- &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Tant triee[i] différent de caractère de fin de chaîne&lt;br /&gt;
    mot_origine &amp;lt;- mot_origine + triee[i]&lt;br /&gt;
    i &amp;lt;- indice de la lettre dans la transformée de Burrows-Wheeler (si 2eme &amp;quot;a&amp;quot; dans triee, alors prendre l&#039;indice du 2eme &amp;quot;a&amp;quot; dans bw)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cet algorithme ne garde pas le cactère de fin de chaîne&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de retrouver la chaîne d&#039;origine à partir de la transformée (garde le caractère de fin de chaîne) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def deconvertion_bw(bw: str) -&amp;gt; str:&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1]) &lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(len(bw)):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return mot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il est donc possible de transformer une chaîne de caractères en une autre qui peut être facilement compressée et de faire le chemin inverse.&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
Il est possible de passer du tableau de suffixes à la transformée de Burrows-Wheeler et inversement car les deux commencent de la même manière : prendre les suffixes d&#039;une chaîne et les trier dans l&#039;ordre alphabétique. Le tableau de suffixes stocke ensuite l&#039;information sous la forme d&#039;un tableau d&#039;indices tandis que la transformée de Burrows-Wheeler sous la forme d&#039;une chaîne de caractère.&lt;br /&gt;
&lt;br /&gt;
La passage de tableau de suffixes à transformée de Burrows-Wheeler est simple. Il suffit de prendre la lettre à l&#039;indice précédant le début du suffixe dans l&#039;ordre du tableau de suffixe (si l&#039;indice du suffixe est 0, alors il faut prendre la dernière lettre de la chaîne).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def sa_en_bw(suffix_array: tuple) -&amp;gt; str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le suffix array en transformé de Burrows Wheeler&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mot = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([mot[(i-1) % taille_mot] for i in array])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour retrouver le tableau de suffixes à l&#039;aide de la transformée de Burrows-Wheeler, il suffit d&#039;appliquer l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sa &amp;lt;- tableau de la taille de la transformée - 1&lt;br /&gt;
bw &amp;lt;- transformée de Burrows-Wheeler&lt;br /&gt;
triee &amp;lt;- bw trié dans l&#039;ordre alphabétique&lt;br /&gt;
i &amp;lt;- indice du caractère de fin de chaîne dans la transformée&lt;br /&gt;
mot_origine &amp;lt;- &amp;quot;&amp;quot;&lt;br /&gt;
j &amp;lt;- 0&lt;br /&gt;
&lt;br /&gt;
Tant triee[i] différent de caractère de fin de chaîne&lt;br /&gt;
    mot_origine &amp;lt;- mot_origine + triee[i]&lt;br /&gt;
    sa[i] = j&lt;br /&gt;
    i &amp;lt;- indice de la lettre dans la transformée de Burrows-Wheeler (si 2eme &amp;quot;a&amp;quot; dans triee, alors prendre l&#039;indice du 2eme &amp;quot;a&amp;quot; dans bw)&lt;br /&gt;
    j &amp;lt;- j + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cet algorithme ne garde pas le caractère de fin de chaîne.&lt;br /&gt;
&lt;br /&gt;
Le passage de la transformée au tableau de suffixes se fait via le code suivant en Python (en gardant le caractère de fin de chaîne) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def bw_en_sa(bw: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le transformé de Burrows Wheeler en suffix array&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille = len(bw)&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1])&lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    sa = [0 for j in range(taille)]&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(taille):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        sa[indice] = i&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return (mot, sa)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On a donc la possibilité de transformer une chaîne de caractère afin de faire qu&#039;elle prenne moins de place et avec la chaîne transformée, générer une structure permettant de faire efficacement une recherche de patern dans une chaîne de caractère.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Comparaison ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_temps_constructions_structures.png|500px|center|thumb|Graphique du temps de génération des structures en fonction de la taille de la chaîne transformée]]&lt;br /&gt;
&lt;br /&gt;
Le graphique ci-dessus nous montre que poure des chaînes de caractères très grandes, il n&#039;est pas avisé d&#039;utiliser un Trie pour la recherche de patern, ni même un arbre de suffixes utilisant le Trie pour se générer. Le choix repose donc entre l&#039;arbre de suffixe (générer sans Trie) et le tableau de suffixe, cependant nous pouvons voir sur le graphique que la tableau de suffixes tend à être plus efficace et rapide à construire que l&#039;arbre de suffixes lorsque la chaîne de caractère est grandissante. Il peut donc être justicieux d&#039;utiliser l&#039;arbre de suffixes plutôt que le tableau de suffixes pour sa rapidité de construction et son stockage plus efficace bien qu&#039;il soit un peu moins efficace pour rechercher un patern.&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_temps_constructions_structures.png&amp;diff=15954</id>
		<title>Fichier:Visi 201 temps constructions structures.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_temps_constructions_structures.png&amp;diff=15954"/>
		<updated>2025-05-15T21:02:32Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15948</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15948"/>
		<updated>2025-05-15T14:23:54Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Passage entre tableau de suffixes et transformée de Burrows-Wheeler */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_array_bananas.png||thumb|right|Tableau de suffixes de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes est une structure légère qui représente indirectement tous les suffixes d&#039;une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
Il stocke chaque indice de début de suffixe dans l&#039;ordre alphabétique des suffixes.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour le créer, il suffit de créer un tableau contenant tous les indices de la chaîne d&#039;origine et de trier la liste suivant les suffixes commençant aux l&#039;indices.&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans le tableau de suffixes, il suffit d&#039;utiliser la recherche dichotomique, ce qui donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes présente comme avantages : sa taille (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine), sa rapidité de construction (&amp;lt;math&amp;gt;nlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine) et sa rapidité de recherche (&amp;lt;math&amp;gt;mlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern et &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Cependant bien que la recherche est plus longue dans de très longues chaînes de caractères avec le tableau de suffixe qu&#039;avec le Trie ou l&#039;arbre de suffixe, la taille de la structure est un aspect non-négligeable pour la recherche de patern.&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_transformee_bw_bananas.png||thumb|right|Transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
La transformée de Burrows-Wheeler permet de transformer une chaîne de caractère en une autre ayant pour propriété que les caractères semblables éloignés dans la chaîne d&#039;origine se retrouvent plus fréquemment collés (par exemple, la transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot; est &amp;quot;sbnn$aaa&amp;quot;). Cette propriété permet de faciliter la compression de la chaîne grâce aux répétitions de caractères.&lt;br /&gt;
&lt;br /&gt;
Pour faire la transformée d&#039;une chaîne, il faut prendre chaque suffixe de la chaîne d&#039;origine, recommencer à la fin de ceux-ci la chaîne jusqu&#039;à ce qu&#039;il y ait le même nombre de caractère dans la chaîne d&#039;origine et dans celle-ci, trier les chaînes dans l&#039;ordre alphabétique, récupérer la dernière lettre de chaque chaîne dans l&#039;ordre.&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de générer la transformée de Burrows-Wheeler :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EOS = chr(28)&lt;br /&gt;
def convertion_bw(mot: str) -&amp;gt; str:&lt;br /&gt;
    mot = mot + EOS&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    tab = [mot[i:] + mot[:i] for i in range(taille_mot)]&lt;br /&gt;
    tab.sort()&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([ligne[taille_mot - 1] for ligne in tab])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La transformée est réversible en connaissant le dernier caractère de la chaîne d&#039;origine. &lt;br /&gt;
Le code Python suivant permet de retrouver la chaîne d&#039;origine à partir de la transformée :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def deconvertion_bw(bw: str) -&amp;gt; str:&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1]) &lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(len(bw)):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return mot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il est donc possible de transformer une chaîne de caractères en une autre qui peut être facilement compressée et de faire le chemin inverse.&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
Il est possible de passer du tableau de suffixes à la transformée de Burrows-Wheeler et inversement car les deux commencent de la même manière : prendre les suffixes d&#039;une chaîne et les trier dans l&#039;ordre alphabétique. Le tableau de suffixes stocke ensuite l&#039;information sous la forme d&#039;un tableau d&#039;indices tandis que la transformée de Burrows-Wheeler sous la forme d&#039;une chaîne de caractère.&lt;br /&gt;
&lt;br /&gt;
La passage de tableau de suffixes à transformée de Burrows-Wheeler est simple. Il suffit de prendre la lettre à l&#039;indice précédant le début du suffixe dans l&#039;ordre du tableau de suffixe (si l&#039;indice du suffixe est 0, alors il faut prendre la dernière lettre de la chaîne).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def sa_en_bw(suffix_array: tuple) -&amp;gt; str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le suffix array en transformé de Burrows Wheeler&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mot = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([mot[(i-1) % taille_mot] for i in array])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le passage de la transformée au tableau de suffixes se fait via le code suivant en Python :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def bw_en_sa(bw: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme le transformé de Burrows Wheeler en suffix array&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille = len(bw)&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1])&lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    sa = [0 for j in range(taille)]&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(taille):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        sa[indice] = i&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return (mot, sa)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On a donc la possibilité de transformer une chaîne de caractère afin de faire qu&#039;elle prenne moins de place et avec la chaîne transformée, générer une structure permettant de faire efficacement une recherche de patern dans une chaîne de caractère.&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15947</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15947"/>
		<updated>2025-05-15T14:11:06Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Transformée de Burrows-Wheeler */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_array_bananas.png||thumb|right|Tableau de suffixes de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes est une structure légère qui représente indirectement tous les suffixes d&#039;une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
Il stocke chaque indice de début de suffixe dans l&#039;ordre alphabétique des suffixes.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour le créer, il suffit de créer un tableau contenant tous les indices de la chaîne d&#039;origine et de trier la liste suivant les suffixes commençant aux l&#039;indices.&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans le tableau de suffixes, il suffit d&#039;utiliser la recherche dichotomique, ce qui donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes présente comme avantages : sa taille (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine), sa rapidité de construction (&amp;lt;math&amp;gt;nlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine) et sa rapidité de recherche (&amp;lt;math&amp;gt;mlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern et &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Cependant bien que la recherche est plus longue dans de très longues chaînes de caractères avec le tableau de suffixe qu&#039;avec le Trie ou l&#039;arbre de suffixe, la taille de la structure est un aspect non-négligeable pour la recherche de patern.&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_transformee_bw_bananas.png||thumb|right|Transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
La transformée de Burrows-Wheeler permet de transformer une chaîne de caractère en une autre ayant pour propriété que les caractères semblables éloignés dans la chaîne d&#039;origine se retrouvent plus fréquemment collés (par exemple, la transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot; est &amp;quot;sbnn$aaa&amp;quot;). Cette propriété permet de faciliter la compression de la chaîne grâce aux répétitions de caractères.&lt;br /&gt;
&lt;br /&gt;
Pour faire la transformée d&#039;une chaîne, il faut prendre chaque suffixe de la chaîne d&#039;origine, recommencer à la fin de ceux-ci la chaîne jusqu&#039;à ce qu&#039;il y ait le même nombre de caractère dans la chaîne d&#039;origine et dans celle-ci, trier les chaînes dans l&#039;ordre alphabétique, récupérer la dernière lettre de chaque chaîne dans l&#039;ordre.&lt;br /&gt;
&lt;br /&gt;
Le code Python suivant permet de générer la transformée de Burrows-Wheeler :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
EOS = chr(28)&lt;br /&gt;
def convertion_bw(mot: str) -&amp;gt; str:&lt;br /&gt;
    mot = mot + EOS&lt;br /&gt;
    taille_mot = len(mot)&lt;br /&gt;
    tab = [mot[i:] + mot[:i] for i in range(taille_mot)]&lt;br /&gt;
    tab.sort()&lt;br /&gt;
    return &amp;quot;&amp;quot;.join([ligne[taille_mot - 1] for ligne in tab])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La transformée est réversible en connaissant le dernier caractère de la chaîne d&#039;origine. &lt;br /&gt;
Le code Python suivant permet de retrouver la chaîne d&#039;origine à partir de la transformée :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def deconvertion_bw(bw: str) -&amp;gt; str:&lt;br /&gt;
    chaine_triee = list(enumerate(list(bw)))&lt;br /&gt;
    chaine_triee.sort(key=lambda e: e[1]) &lt;br /&gt;
    indice = bw.find(EOS)&lt;br /&gt;
    mot = &amp;quot;&amp;quot;&lt;br /&gt;
    i = 0&lt;br /&gt;
    for j in range(len(bw)):&lt;br /&gt;
        caractere = chaine_triee[indice][1]&lt;br /&gt;
        mot += caractere&lt;br /&gt;
        i += 1&lt;br /&gt;
        indice = chaine_triee[indice][0]&lt;br /&gt;
    return mot&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il est donc possible de transformer une chaîne de caractères en une autre qui peut être facilement compressée et de faire le chemin inverse.&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_transformee_bw_bananas.png&amp;diff=15946</id>
		<title>Fichier:Visi 201 transformee bw bananas.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_transformee_bw_bananas.png&amp;diff=15946"/>
		<updated>2025-05-15T13:42:25Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
transformée de Burrows-Wheeler de &amp;quot;bananas$&amp;quot;&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15945</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15945"/>
		<updated>2025-05-15T13:34:57Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Tableau de suffixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_array_bananas.png||thumb|right|Tableau de suffixes de &amp;quot;bananas$&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes est une structure légère qui représente indirectement tous les suffixes d&#039;une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
Il stocke chaque indice de début de suffixe dans l&#039;ordre alphabétique des suffixes.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour le créer, il suffit de créer un tableau contenant tous les indices de la chaîne d&#039;origine et de trier la liste suivant les suffixes commençant aux l&#039;indices.&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans le tableau de suffixes, il suffit d&#039;utiliser la recherche dichotomique, ce qui donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le tableau de suffixes présente comme avantages : sa taille (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine), sa rapidité de construction (&amp;lt;math&amp;gt;nlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine) et sa rapidité de recherche (&amp;lt;math&amp;gt;mlog(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern et &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
Cependant bien que la recherche est plus longue dans de très longues chaînes de caractères avec le tableau de suffixe qu&#039;avec le Trie ou l&#039;arbre de suffixe, la taille de la structure est un aspect non-négligeable pour la recherche de patern.&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_array_bananas.png&amp;diff=15944</id>
		<title>Fichier:Visi 201 suffix array bananas.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_array_bananas.png&amp;diff=15944"/>
		<updated>2025-05-15T13:08:19Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Tableau de suffixes de bananas&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
Tableau de suffixes de bananas&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15943</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15943"/>
		<updated>2025-05-15T12:37:41Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Trie (Arbre de préfixes) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
La tableau de suffixes est une structure prenant encore moins de place que l&#039;arbre de suffixes et qui permet aussi une recherche plus rapide que celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le principe du tableau de suffixes est de stocker chaque suffixe d&#039;une chaîne de caractère par l&#039;indice de début du suffixe dans l&#039;ordre alphabétique du suffixe (donc &amp;quot;abra&amp;quot; viendra avant &amp;quot;ada&amp;quot; car &amp;quot;b&amp;quot; vient avant &amp;quot;d&amp;quot;). Ainsi, une chaîne de &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; caractères produira un tableau de suffixes à &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; éléments.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour un chaîne de caractère &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; de taille n, on génère le tableau &amp;lt;code&amp;gt;T=[0, 1, 2, ..., n-1]&amp;lt;/code&amp;gt; et on trie chaque élément de &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; en mettant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt; avant &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; si et seulement si &amp;lt;code&amp;gt;c[T[a]:] &amp;lt; c[T[b]:]&amp;lt;/code&amp;gt;, sinon &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; viendra avant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme de trie choisit définira la complexité temporel de construction de la structure.&lt;br /&gt;
&lt;br /&gt;
La recherche dans le tableau de suffixes est plus efficace que dans une autre des structures présentées car nous avons un tableau totalement trié, ce qui nous permet d&#039;utiliser la dichotomie afin de rechercher un paterne dans la chaîne de caractère (complexité de &amp;lt;code&amp;gt;O(nlog(n))&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Voici, ci-dessous, l&#039;algorithme de la recherche dans le tableau de suffixes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15942</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15942"/>
		<updated>2025-05-15T12:29:18Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Arbre de suffixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &#039;&#039;O(n^2)&#039;&#039; (&#039;&#039;n&#039;&#039;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &#039;&#039;O(n^2)&#039;&#039; (&#039;&#039;n&#039;&#039;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad_imp.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec longueurs]]&lt;br /&gt;
[[Fichier:Visi_201_suffix_tree_abracadabradad.png|400px|thumb|right|Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; pour lecture]]&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Trie, il faut réduire les chemins uniques (ceux pour lesquels les noeuds successifs n&#039;ont qu&#039;un unique enfant) en les rassemblant en un unique noeud (dans &amp;quot;panpan&amp;quot;, on regrouperait, entre autres, &amp;quot;pan&amp;quot;, &amp;quot;an&amp;quot; et &amp;quot;npan&amp;quot;). Cependant, la même quantité d&#039;information est toujours stockée mais d&#039;une façon différente. Pour résoudre ce problème, il faut faut considérer chaque noeud comme une information de 2 nombres sur la chaîne d&#039;origine : l&#039;indice de début de la sous-chaîne et la longueur ou l&#039;indice de fin de celle-ci. L&#039;indice de début doit toujours être supérieur à l&#039;indice auquel se fini la sous-chaîne représenté sur la branche parente (à la racine, c&#039;est l&#039;indice 0) et il doit être le plus petit possible en même temps.&lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de générer cette arbre d&#039;autre manière telle que la suivante en Python qui utilise en clef l&#039;indice du début du suffixe et sa taille :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def convertion_tree(mot: str) -&amp;gt; tuple:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractères en arbre de suffixe&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    arbre = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        ajouter_tree(arbre, mot, i)&lt;br /&gt;
    return (mot, arbre)&lt;br /&gt;
&lt;br /&gt;
def ajouter_tree(branche: dict, mot: str, offset: int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Ajouter une chaîne de caractères dans un arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_total_keys = len(keys)&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    est_dedans = mot[offset:] == &amp;quot;&amp;quot;&lt;br /&gt;
    longueur_mot_a_placer = len(mot) - offset&lt;br /&gt;
&lt;br /&gt;
    while nb_key &amp;lt; nb_total_keys and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
&lt;br /&gt;
        if mot[key[0]] == mot[offset]:&lt;br /&gt;
            nb_egaux = 1&lt;br /&gt;
            stop = False&lt;br /&gt;
            while nb_egaux &amp;lt; min(key[1], longueur_mot_a_placer) and not stop:&lt;br /&gt;
                if mot[key[0] + nb_egaux] != mot[offset + nb_egaux]:&lt;br /&gt;
                    stop = True&lt;br /&gt;
                else:&lt;br /&gt;
                    nb_egaux += 1&lt;br /&gt;
            &lt;br /&gt;
            if nb_egaux != longueur_mot_a_placer:&lt;br /&gt;
                if nb_egaux == key[1]:&lt;br /&gt;
                    ajouter_tree(branche[key], mot, offset + nb_egaux)&lt;br /&gt;
                else:&lt;br /&gt;
                    branche[(key[0], nb_egaux)] = {&lt;br /&gt;
                        (key[0] + nb_egaux, key[1] - nb_egaux): branche[key],&lt;br /&gt;
                        (offset + nb_egaux, longueur_mot_a_placer - nb_egaux): {}&lt;br /&gt;
                    }&lt;br /&gt;
                    branche.pop(key)&lt;br /&gt;
            est_dedans = True&lt;br /&gt;
&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    &lt;br /&gt;
    if not est_dedans:&lt;br /&gt;
        branche[(offset, len(mot) - offset)] = {}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes, tout comme le Trie, créé des chemins uniques pour chaque suffixe de la chaîne d&#039;origine. En utilisant ce principe, il est possible d&#039;effectuer une recherche dans la structure en suivant les règles suivantes :&lt;br /&gt;
* S&#039;il existe une branche dont la lettre à l&#039;indice du début du suffixe dans la chaîne d&#039;origine correspond à la première lettre du patern : &lt;br /&gt;
** Si le patern est inclus dans la sous-chaîne, le patern est dans la chaîne d&#039;origine&lt;br /&gt;
** Si la sous chaîne est incluse dans le patern, cherche la suite du patern dans le sous-arbre correspondant à la branche&lt;br /&gt;
** Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne d&#039;origine&lt;br /&gt;
&lt;br /&gt;
Ce qui nous donne le code Python suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tree(arbre: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans l&#039;arbre&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return est_dans_branche(arbre[1], arbre[0], mot)&lt;br /&gt;
&lt;br /&gt;
def est_dans_branche(branche: dict, mot_origine: str, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la chaîne de caractères est contenue dans la branche&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    est_dedans = mot == &amp;quot;&amp;quot;&lt;br /&gt;
    fin = False&lt;br /&gt;
    keys = list(branche.keys())&lt;br /&gt;
    nb_key = 0&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
    while nb_key &amp;lt; len(keys) and not fin and not est_dedans:&lt;br /&gt;
        key = keys[nb_key]&lt;br /&gt;
        if mot_origine[key[0]] == mot[0]:&lt;br /&gt;
            if key[1] == longueur_mot:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + key[1]] == mot&lt;br /&gt;
            elif longueur_mot &amp;lt; key[1]:&lt;br /&gt;
                est_dedans = mot_origine[key[0] : key[0] + longueur_mot] == mot&lt;br /&gt;
            else:&lt;br /&gt;
                est_dedans = est_dans_branche(branche[key], mot_origine, mot[key[1]:])&lt;br /&gt;
            fin = True&lt;br /&gt;
        nb_key += 1&lt;br /&gt;
    return est_dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes présente plusieurs avantages :&lt;br /&gt;
* Il a une taille réduite (&amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne d&#039;origine&lt;br /&gt;
* Simple à comprendre&lt;br /&gt;
* Recherche rapide (&amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt; la taille du patern)&lt;br /&gt;
* Peut être construit grâce au Trie&lt;br /&gt;
&lt;br /&gt;
Mais celui-ci présente aussi des désavantages :&lt;br /&gt;
* Plus complexe à implémenter&lt;br /&gt;
* Sa taille reste grande : la structure est lourde, surtout pour de grandes chaînes de caractères &lt;br /&gt;
&lt;br /&gt;
Il est aussi possible de construire l&#039;arbre de suffixes de manière plus efficace (construction en &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; au lieu de &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; avec &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; la taille de la chaîne de caractères d&#039;origine) en utilisant par exemple l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen].&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
La tableau de suffixes est une structure prenant encore moins de place que l&#039;arbre de suffixes et qui permet aussi une recherche plus rapide que celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le principe du tableau de suffixes est de stocker chaque suffixe d&#039;une chaîne de caractère par l&#039;indice de début du suffixe dans l&#039;ordre alphabétique du suffixe (donc &amp;quot;abra&amp;quot; viendra avant &amp;quot;ada&amp;quot; car &amp;quot;b&amp;quot; vient avant &amp;quot;d&amp;quot;). Ainsi, une chaîne de &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; caractères produira un tableau de suffixes à &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; éléments.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour un chaîne de caractère &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; de taille n, on génère le tableau &amp;lt;code&amp;gt;T=[0, 1, 2, ..., n-1]&amp;lt;/code&amp;gt; et on trie chaque élément de &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; en mettant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt; avant &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; si et seulement si &amp;lt;code&amp;gt;c[T[a]:] &amp;lt; c[T[b]:]&amp;lt;/code&amp;gt;, sinon &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; viendra avant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme de trie choisit définira la complexité temporel de construction de la structure.&lt;br /&gt;
&lt;br /&gt;
La recherche dans le tableau de suffixes est plus efficace que dans une autre des structures présentées car nous avons un tableau totalement trié, ce qui nous permet d&#039;utiliser la dichotomie afin de rechercher un paterne dans la chaîne de caractère (complexité de &amp;lt;code&amp;gt;O(nlog(n))&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Voici, ci-dessous, l&#039;algorithme de la recherche dans le tableau de suffixes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_tree_abracadabradad_imp.png&amp;diff=15941</id>
		<title>Fichier:Visi 201 suffix tree abracadabradad imp.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_tree_abracadabradad_imp.png&amp;diff=15941"/>
		<updated>2025-05-15T09:59:38Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec les nombres&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec les nombres&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_tree_abracadabradad.png&amp;diff=15940</id>
		<title>Fichier:Visi 201 suffix tree abracadabradad.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_suffix_tree_abracadabradad.png&amp;diff=15940"/>
		<updated>2025-05-15T09:42:11Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec les lettres&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
Arbre de suffixes de &amp;quot;abracadabradad&amp;quot; avec les lettres&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15939</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15939"/>
		<updated>2025-05-15T09:29:54Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Réécriture introduction, recherche naïve, trie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Tuteur : TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de patern dans une chaîne de caractères est un problème récurrent qui peut rapidement poser problème lorsque l&#039;on cherche plusieurs fois dans une chaîne de caractères de grande taille.&lt;br /&gt;
&lt;br /&gt;
Un patern est aussi une chaîne de caractères.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf pour rechercher une chaîne de caractères dans une autre consiste à regarder dans l&#039;ordre la chaîne de caractères dans laquelle on cherche la sous-chaîne et de vérifier si les caractères correspondent ou non. &lt;br /&gt;
&lt;br /&gt;
Nous pouvons l&#039;implémenter comme suivant en Python :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans(sous_chaine: str, chaine: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si la sous chaîne est dans la chaîne&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    dedans = False&lt;br /&gt;
    i = 0&lt;br /&gt;
    while i &amp;lt; len(chaine) and not dedans:&lt;br /&gt;
        offset = 0&lt;br /&gt;
        stop = False&lt;br /&gt;
        while i + offset &amp;lt; len(chaine) and offset &amp;lt; len(sous_chaine) and not stop:&lt;br /&gt;
            if sous_chaine[offset] != chaine[i+offset]:&lt;br /&gt;
                stop = True&lt;br /&gt;
            offset += 1&lt;br /&gt;
        if not stop:&lt;br /&gt;
            dedans = True&lt;br /&gt;
        i += 1&lt;br /&gt;
    return dedans&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant, bien qu&#039;il puisse être optimisé et évitant certaines comparaisons, il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc qu&#039;il n&#039;est pas efficace sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données différentes qui ont pour but de représenter tous les suffixes existant d&#039;une chaîne de caractère et qui permet de les identifier de manière unique. Ces conditions impliquent que toutes les sous-chaînes de la chaîne décomposée seront représenté à l&#039;intérieur de la structure et seront plus facilement extrayables.&lt;br /&gt;
&lt;br /&gt;
Pour effectuer la recherche du patern dans notre chaîne de caractère grâce aux différentes structures, il faut partir du principe que chaque sous-chaîne dans la chaîne d&#039;origine est toujours préfixe d&#039;un suffixe.&lt;br /&gt;
&lt;br /&gt;
=== Trie (Arbre de préfixes) ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Visi_201_trie_abracadabradad.png|200px|thumb|right|Trie de &amp;quot;abracadabradad&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure permettant de représenter tous les suffixes d&#039;une chaîne de caractères sous la forme d&#039;un arbre. Chaque chemin allant de la racine de l&#039;arbre à une de ses feuilles représente un suffixe différent. Chaque branche de l&#039;arbre est une lettre et mène soit à une feuille soit à un sous Trie qui permet la représentation de la suite du suffixe.&lt;br /&gt;
&lt;br /&gt;
Il est possible de créer le Trie en ajoutant successivement chaque suffixe de la chaîne de caractère à la racine de l&#039;arbre en respectant les règles suivantes :&lt;br /&gt;
* Si une branche de l&#039;arbre correspond à la première lettre du suffixe à ajouter, il faut ajouter la suite du suffixe dans le sous-arbre de cette branche&lt;br /&gt;
* Sinon créer une branche correspondant à la première lettre du suffixe à ajouter et ajouter la suite du suffixe dans le sous-arbre de cette nouvelle branche&lt;br /&gt;
&lt;br /&gt;
Cela nous donne le code python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La recherche dans le Trie consiste à regarder s&#039;il existe un chemin pour lequel chaque valeur des branches dans l&#039;ordre correspond à chaque lettre du patern recherché.&lt;br /&gt;
&lt;br /&gt;
On suit donc les règles suivantes:&lt;br /&gt;
* Si une branche du Trie correspond à la première lettre du patern, chercher le patern dans le sous-Trie correspondant de la branche&lt;br /&gt;
* Sinon, le patern n&#039;est pas dans la chaîne&lt;br /&gt;
&lt;br /&gt;
Cet algorithme donne le code Python suivant :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cette structure présente plusieurs avantages :&lt;br /&gt;
* Facile à comprendre (construction et recherche)&lt;br /&gt;
* Facile à implémenter&lt;br /&gt;
* Recherche en &amp;lt;math&amp;gt;O(m)&amp;lt;/math&amp;gt; (&amp;lt;math&amp;gt;m&amp;lt;/math&amp;gt;: la longueur de la chaîne recherchée)&lt;br /&gt;
&lt;br /&gt;
Cependant elle présente aussi plusieurs désavantages :&lt;br /&gt;
* Construction en &#039;&#039;O(n^2)&#039;&#039; (&#039;&#039;n&#039;&#039;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
* Complexité spatiale en &#039;&#039;O(n^2)&#039;&#039; (&#039;&#039;n&#039;&#039;: la longueur de la chaîne qui génère l&#039;arbre)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ainsi que le nombre de lettre parcourue à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche ou l&#039;indice de fin de cette nouvelle branche. L&#039;utilisation de l&#039;indice de fin de la branche ou du nombre de lettres dans la branche est un choix qui doit être le même dans toutes les branches de l&#039;arbre. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne règle pas le problème de la construction longue lorsque les chaînes de caractère sont grandes. Il est cependant possible de régler ce problème en construisant l&#039;arbre grâce à l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen], qui permet de construire l&#039;arbre de suffixes avec une complexité linéaire.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans l&#039;arbre de suffixes la présence d&#039;un paterne, il suffit de suivre le même algorithme que celui du Trie en remplaçant chaque indice par sa lettre à la position de l&#039;indice dans le mot ainsi que de prendre en compte que la branche n&#039;est pas forcément de longueur 1, mais peut être plus longue.&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
La tableau de suffixes est une structure prenant encore moins de place que l&#039;arbre de suffixes et qui permet aussi une recherche plus rapide que celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le principe du tableau de suffixes est de stocker chaque suffixe d&#039;une chaîne de caractère par l&#039;indice de début du suffixe dans l&#039;ordre alphabétique du suffixe (donc &amp;quot;abra&amp;quot; viendra avant &amp;quot;ada&amp;quot; car &amp;quot;b&amp;quot; vient avant &amp;quot;d&amp;quot;). Ainsi, une chaîne de &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; caractères produira un tableau de suffixes à &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; éléments.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour un chaîne de caractère &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; de taille n, on génère le tableau &amp;lt;code&amp;gt;T=[0, 1, 2, ..., n-1]&amp;lt;/code&amp;gt; et on trie chaque élément de &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; en mettant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt; avant &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; si et seulement si &amp;lt;code&amp;gt;c[T[a]:] &amp;lt; c[T[b]:]&amp;lt;/code&amp;gt;, sinon &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; viendra avant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme de trie choisit définira la complexité temporel de construction de la structure.&lt;br /&gt;
&lt;br /&gt;
La recherche dans le tableau de suffixes est plus efficace que dans une autre des structures présentées car nous avons un tableau totalement trié, ce qui nous permet d&#039;utiliser la dichotomie afin de rechercher un paterne dans la chaîne de caractère (complexité de &amp;lt;code&amp;gt;O(nlog(n))&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Voici, ci-dessous, l&#039;algorithme de la recherche dans le tableau de suffixes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_trie_abracadabradad.png&amp;diff=15938</id>
		<title>Fichier:Visi 201 trie abracadabradad.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Visi_201_trie_abracadabradad.png&amp;diff=15938"/>
		<updated>2025-05-15T08:37:15Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Trie (Arbre de Préfixes) de &amp;quot;abracadabradad&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Description ==&lt;br /&gt;
Trie (Arbre de Préfixes) de &amp;quot;abracadabradad&amp;quot;&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15863</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15863"/>
		<updated>2025-05-05T20:45:51Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Tableau de suffixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ainsi que le nombre de lettre parcourue à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche ou l&#039;indice de fin de cette nouvelle branche. L&#039;utilisation de l&#039;indice de fin de la branche ou du nombre de lettres dans la branche est un choix qui doit être le même dans toutes les branches de l&#039;arbre. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne règle pas le problème de la construction longue lorsque les chaînes de caractère sont grandes. Il est cependant possible de régler ce problème en construisant l&#039;arbre grâce à l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen], qui permet de construire l&#039;arbre de suffixes avec une complexité linéaire.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans l&#039;arbre de suffixes la présence d&#039;un paterne, il suffit de suivre le même algorithme que celui du Trie en remplaçant chaque indice par sa lettre à la position de l&#039;indice dans le mot ainsi que de prendre en compte que la branche n&#039;est pas forcément de longueur 1, mais peut être plus longue.&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixes ===&lt;br /&gt;
&lt;br /&gt;
La tableau de suffixes est une structure prenant encore moins de place que l&#039;arbre de suffixes et qui permet aussi une recherche plus rapide que celui-ci.&lt;br /&gt;
&lt;br /&gt;
Le principe du tableau de suffixes est de stocker chaque suffixe d&#039;une chaîne de caractère par l&#039;indice de début du suffixe dans l&#039;ordre alphabétique du suffixe (donc &amp;quot;abra&amp;quot; viendra avant &amp;quot;ada&amp;quot; car &amp;quot;b&amp;quot; vient avant &amp;quot;d&amp;quot;). Ainsi, une chaîne de &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; caractères produira un tableau de suffixes à &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; éléments.&lt;br /&gt;
&lt;br /&gt;
Ainsi, pour un chaîne de caractère &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; de taille n, on génère le tableau &amp;lt;code&amp;gt;T=[0, 1, 2, ..., n-1]&amp;lt;/code&amp;gt; et on trie chaque élément de &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; en mettant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt; avant &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; si et seulement si &amp;lt;code&amp;gt;c[T[a]:] &amp;lt; c[T[b]:]&amp;lt;/code&amp;gt;, sinon &amp;lt;code&amp;gt;T[b]&amp;lt;/code&amp;gt; viendra avant &amp;lt;code&amp;gt;T[a]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme de trie choisit définira la complexité temporel de construction de la structure.&lt;br /&gt;
&lt;br /&gt;
La recherche dans le tableau de suffixes est plus efficace que dans une autre des structures présentées car nous avons un tableau totalement trié, ce qui nous permet d&#039;utiliser la dichotomie afin de rechercher un paterne dans la chaîne de caractère (complexité de &amp;lt;code&amp;gt;O(n)=nlog(n)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Voici, ci-dessous, l&#039;algorithme de la recherche dans le tableau de suffixes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_suffix_array(suffix_array: tuple, mot: str) -&amp;gt; bool:&lt;br /&gt;
    res = False&lt;br /&gt;
&lt;br /&gt;
    mot_origine = suffix_array[0]&lt;br /&gt;
    array = suffix_array[1]&lt;br /&gt;
    longueur_mot_origine = len(mot_origine)&lt;br /&gt;
    longueur_mot = len(mot)&lt;br /&gt;
&lt;br /&gt;
    debut = 0&lt;br /&gt;
    fin = len(array) - 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    while debut &amp;lt;= fin and not res:&lt;br /&gt;
        mil = (debut + fin) // 2&lt;br /&gt;
        val_mil = array[mil]&lt;br /&gt;
        sous_mot = mot_origine[val_mil: min(val_mil + longueur_mot, longueur_mot_origine)]&lt;br /&gt;
&lt;br /&gt;
        if mot == sous_mot:&lt;br /&gt;
            res = True&lt;br /&gt;
        elif mot &amp;lt; sous_mot:&lt;br /&gt;
            fin = mil - 1&lt;br /&gt;
        else:&lt;br /&gt;
            debut = mil + 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15862</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15862"/>
		<updated>2025-05-05T20:20:02Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Arbre de suffixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ainsi que le nombre de lettre parcourue à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche ou l&#039;indice de fin de cette nouvelle branche. L&#039;utilisation de l&#039;indice de fin de la branche ou du nombre de lettres dans la branche est un choix qui doit être le même dans toutes les branches de l&#039;arbre. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne règle pas le problème de la construction longue lorsque les chaînes de caractère sont grandes. Il est cependant possible de régler ce problème en construisant l&#039;arbre grâce à l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen], qui permet de construire l&#039;arbre de suffixes avec une complexité linéaire.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de rechercher dans l&#039;arbre de suffixes la présence d&#039;un paterne, il suffit de suivre le même algorithme que celui du Trie en remplaçant chaque indice par sa lettre à la position de l&#039;indice dans le mot ainsi que de prendre en compte que la branche n&#039;est pas forcément de longueur 1, mais peut être plus longue.&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15861</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15861"/>
		<updated>2025-05-05T20:15:12Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Arbre de suffixe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixes ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ainsi que le nombre de lettre parcourue à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche ou l&#039;indice de fin de cette nouvelle branche. L&#039;utilisation de l&#039;indice de fin de la branche ou du nombre de lettres dans la branche est un choix qui doit être le même dans toutes les branches de l&#039;arbre. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne règle pas le problème de la construction longue lorsque les chaînes de caractère sont grandes. Il est cependant possible de régler ce problème en construisant l&#039;arbre grâce à l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen], qui permet de construire l&#039;arbre de suffixes avec une complexité linéaire.&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15860</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15860"/>
		<updated>2025-05-05T20:15:05Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Arbre de suffixe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixe ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ainsi que le nombre de lettre parcourue à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche ou l&#039;indice de fin de cette nouvelle branche. L&#039;utilisation de l&#039;indice de fin de la branche ou du nombre de lettres dans la branche est un choix qui doit être le même dans toutes les branches de l&#039;arbre. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne règle pas le problème de la construction longue lorsque les chaînes de caractère sont grandes. Il est cependant possible de régler ce problème en construisant l&#039;arbre grâce à l&#039;[https://fr.wikipedia.org/wiki/Algorithme_d%27Ukkonen algorithme de Ukkonen], qui permet de construire l&#039;arbre de suffixes avec une complexité linéaire.&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15788</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15788"/>
		<updated>2025-04-14T07:55:54Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : /* Arbre de suffixe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixe ===&lt;br /&gt;
&lt;br /&gt;
L&#039;arbre de suffixes est un structure proche du Trie mais qui règle le problème de la taille de la structure.&lt;br /&gt;
&lt;br /&gt;
Pour réduire la taille du Tries, il faut considérer chaque lettre par son plus petit indice dans la chaîne de caractère en avançant à chaque fois l&#039;indice.&lt;br /&gt;
&lt;br /&gt;
Donc en reprenant le Trie de &amp;quot;abracadabradad&amp;quot; et en transformant chaque lettre par son plus petit indice dans la chaîne ansi que le nombre de lettre parcouru à cet endroit (soit toujours 1 actuellement), on obtient l&#039;arbre suivant :&lt;br /&gt;
TRIE MAIS AVEC INDICES ET LONGUEURS&lt;br /&gt;
&lt;br /&gt;
Ensuite, on vérifie pour chaque branche le nombre de sous-branches. S&#039;il n&#039;y a pas de sous-branche, on ne fait rien car c&#039;est une feuille. Si une branche n&#039;a qu&#039;une sous branche, on les combine en précisant d&#039;où l&#039;on part (indice de la branche) et le nombre de lettres contenues dans cette nouvelle branche. &lt;br /&gt;
&lt;br /&gt;
En suivant de principe, on obtient l&#039;arbre suivant pour la chaîne &amp;quot;abracadabradad&amp;quot; :&lt;br /&gt;
TREE&lt;br /&gt;
&lt;br /&gt;
On peut constater que l&#039;arbre est plus petit que le Trie, mais que cependant il est plus compliqué à comprendre. De plus, pour le construire, on utilise le Trie ce qui implique que la construction de la structure est d&#039;une complexité au moins quadratique, ce qui ne nous convient pas.&lt;br /&gt;
&lt;br /&gt;
Il faut donc trouver un moyen de construire l&#039;arbre de suffixes sans utiliser le Trie afin de réduire le temps de construction.&lt;br /&gt;
&lt;br /&gt;
Pour cela, on peut se pencher sur l&#039;algorithme de Ukkonen. Celui-ci consiste à ajouter chaque lettre dans la structure une à une au lieu d&#039;ajouter un suffixe complet d&#039;un coup. Ceci permet de créer tous les suffixes existant de la chaîne dans un arbre de suffixes, et ce de façon linéaire.&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme de Ukkonen est le suivant:&lt;br /&gt;
&lt;br /&gt;
# À chaque feuille de l&#039;arbre on ajoute la lettre actuelle&lt;br /&gt;
# À la racine :&lt;br /&gt;
#; Si la lettre est la première lettre d&#039;une des branches, on notifie que la branche peut être scindée&lt;br /&gt;
#; Si la lettre n&#039;est la première lettre d&#039;aucune des branches, crée une nouvelle branche avec cette lettre&lt;br /&gt;
&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15787</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15787"/>
		<updated>2025-04-13T20:52:48Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Introduction + Algorithme naïf + Trie&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
La recherche de paterne dans une chaîne de caractère est un problème récurrent qui peut rapidement poser problème lorsque la taille de la chaîne augmente.&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Recherche naïve ===&lt;br /&gt;
&lt;br /&gt;
L&#039;algorithme naïf dans la recherche d&#039;un paterne dans une chaîne de caractère consiste à regarder et à comparer successivement les lettres du paternes et de la chaîne. Si les caractères ne sont pas égaux, alors on avance d&#039;un caractère dans la chaîne et on recommence jusqu&#039;à la fin de la chaîne de caractère si on ne trouve pas le paterne.&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cet algorithme est qu&#039;il est facile à comprendre et à implémenter. Cependant il est très lent à l&#039;exécution car il est de complexité quadratique, impliquant donc que cet algorithme n&#039;est pas efficaces sur de très grandes chaînes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de palier à ce problème, il est possible de créer des structures de données complexes créée de façon à représenter tous les suffixes existant d&#039;une chaîne de caractère de façon unique. Cette condition implique que tous les paternes existant dans une chaîne seront représentés, en effet chaque paterne de la chaîne est le préfixe d&#039;au moins un suffixe. &lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
&lt;br /&gt;
Le Trie est un structure complexe permettant de représenter tous les suffixes d&#039;une chaîne de caractère qui est simple.&lt;br /&gt;
&lt;br /&gt;
On peut le représenter comme un arbre pour lequel chaque branche est une lettre. En partant de la racine et en allant jusqu&#039;à n&#039;importe quelle feuille de cet arbre, on obtient un suffixe de la chaîne d&#039;origine.&lt;br /&gt;
&lt;br /&gt;
La façon simple de le construire est d&#039;ajouter successivement les suffixes de la chaîne dans le Trie.&lt;br /&gt;
&lt;br /&gt;
Algorithme :&lt;br /&gt;
# On parcourt chaque lettre du suffixe.&lt;br /&gt;
#: Si dans la position courante il existe un chemin qui a pour clef à cette lettre, on change la position courante en allant dans ce chemin.&lt;br /&gt;
#: Sinon on créé un chemin qui a pour clef la lettre, puis on change la position courante en allant dans ce chemin.&lt;br /&gt;
# On retourne à la racine du Trie.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def tries(mot: str) -&amp;gt; dict:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Transforme une chaîne de caractère en tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = {}&lt;br /&gt;
    for i in range(len(mot)):&lt;br /&gt;
        actuel = res&lt;br /&gt;
        for n in range(i, len(mot)):&lt;br /&gt;
            actuel[mot[n]] = actuel.get(mot[n], {})&lt;br /&gt;
            actuel = actuel[mot[n]]&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Afin de chercher un paterne dans la structure, il suffit de suivre le même principe que pour la création du Trie mise à part pour la création d&#039;un chemin s&#039;il n&#039;existe pas et le retour à la racine (qui est inutile dans le cas présent).&lt;br /&gt;
On considère qu&#039;un paterne est présent dans la structure (et donc dans la chaîne) si la suite de chemins demandée existe dans l&#039;ordre.&lt;br /&gt;
On obtient donc l&#039;algorithme suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_dans_tries(tries: dict, mot: str) -&amp;gt; bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Renvoie si une chaîne de caractère est décomposée dans le tries&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    i = 0&lt;br /&gt;
    actuel = tries&lt;br /&gt;
    while i &amp;lt; len(mot) and res:&lt;br /&gt;
        prochain = actuel.get(mot[i], None)&lt;br /&gt;
        if prochain != None:&lt;br /&gt;
            actuel = prochain&lt;br /&gt;
        else:&lt;br /&gt;
            res = False&lt;br /&gt;
        i += 1&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
L&#039;avantage de cette structure complexe est qu&#039;elle est simple à construire, à implémenter, à comprendre et que la recherche de paterne est rapide.&lt;br /&gt;
Cependant, celle-ci a un défaut : sa construction. En effet, bien que la recherche de paterne soit de complexité linéaire (&#039;&#039;O(m)&#039;&#039; avec &#039;&#039;m&#039;&#039; la longueur du paterne), la construction du Trie est, quant à elle, de complexité quadratique impliquant à nouveau que lorsqu&#039;une chaîne de caractère est très grande, il faille du temps afin d&#039;effectuer la recherche dans la chaîne. De plus, la structure prend beaucoup de place : il est fréquent de voir des branches avec un seul enfant qui pourraient pourtant être regroupés mais qui ne le sont pas.&lt;br /&gt;
&lt;br /&gt;
=== Arbre de suffixe ===&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15735</id>
		<title>&quot;tableau des suffixes&quot; et transformée de Burrows-Wheeler</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=%22tableau_des_suffixes%22_et_transform%C3%A9e_de_Burrows-Wheeler&amp;diff=15735"/>
		<updated>2025-03-09T13:25:23Z</updated>

		<summary type="html">&lt;p&gt;Bogdan : Page créée avec « Étudiant: BOGDAN Benjamin  Chercheur: TAVENAS Sébastien  == Introduction ==  == Recherche dans une chaîne de caractères ==  === Trie === === Arbre de suffixe === === Tableau de suffixe ===  == Transformée de Burrows-Wheeler ==  == Passage entre tableau de suffixes et transformée de Burrows-Wheeler == »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant: BOGDAN Benjamin&lt;br /&gt;
&lt;br /&gt;
Chercheur: TAVENAS Sébastien&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
== Recherche dans une chaîne de caractères ==&lt;br /&gt;
&lt;br /&gt;
=== Trie ===&lt;br /&gt;
=== Arbre de suffixe ===&lt;br /&gt;
=== Tableau de suffixe ===&lt;br /&gt;
&lt;br /&gt;
== Transformée de Burrows-Wheeler ==&lt;br /&gt;
&lt;br /&gt;
== Passage entre tableau de suffixes et transformée de Burrows-Wheeler ==&lt;/div&gt;</summary>
		<author><name>Bogdan</name></author>
	</entry>
</feed>