Corrigé du TD n° 12 1) Nous allons adopter une solution qui se sert d'un tableau pour garder les arêtes étiquetés vers les fils. Au lieu d'exprimer les étiquettes explicitement, nous utiliserons la position d'une arête (= référence à un fils) dans le tableau des arêtes comme son étiquette implicite. On pourra utiliser le code ASCII d'un caractère comme indice du tableau des fils, ce qui est facile mais susceptible de gâcher beaucoup de mémoire, surtout si l'alphabet effectivement utilisé dans le texte est reduit par rapport aux 256 caractères de la table ASCII étendue. Un autre problème est que si notre texte utilise des caractères Unicode, dont le code peut consister en un entier de 16 bits ou plus, on aurait besoin de tableaux trop grands. Une solution à ces deux problèmes consiste en créer au préalable un seul grand tableau contenant, aux positions qui correspondent aux caractères effectivement utilisés dans le texte, des nombres entiers progressifs, de 1 à N, une sorte de codage alternatif, qui seront utilisés comme indices pour accéder aux tableaux des fils. Voici les attributs de la classe ArbreSuff: - profondeur, de type entier : cela nous permet de stocker la profondeur d'un nœud, et donc d'éviter de la recalculer à chaque fois qu'on en a besoin ; - position, de type entier : position de début dans le texte du suffixe qui correspond au chemin de la racine à ce nœud ; on affectera la valeur 0 à la racine (chaîne vide) ; - parent, une référence au nœud parent, de type ArbreSuff ; - fils[N], un tableau de références au sous-arbres, de type ArbreSuff Nous allons prévoir aussi un constructeur simple, qui prend comme argument une référence à un ArbreSuff parent, qui nous permettra de créer des nouveaux nœuds vides. ArbreSuff(ArbreSuff p) parent <- p si parent != NIL profondeur <- parent.profondeur + 1 sinon profondeur <- 0 position <- 0 pour i de 1 à N fils[i] <- NIL 2) Construction de l'arbre des suffixes ArbreSuff(Texte t) self(NIL) # appel au constructeur simple, pour créer la racine ouvCour <- nouvelle Liste() # liste des nœuds ouverts pour l'itération courante ouvCour.ajouter(self) # au début, ouvCour ne contient que la racine pour i de 1 à t.taille() c <- alphabet[t[i]] # conversion ASCII -> code interne ouvSuiv <- nouvelle Liste() # liste des nœuds ouverts pour l'itération suivante ouvSuiv.ajouter(self) # la racine fait toujours partie des nœuds ouverts Itérateur ouverts <- ouvCour.itérateur() # On utilise un itérateur pour parcourir la liste ouvCour tant que ouverts.hasNext() nœud <- ouverts.next() si nœud.fils[c] = NIL nœud.fils[c] <- nouveau ArbreSuff(nœud) nœud.fils[c].position <- i - nœud.profondeur ouvSuiv.ajouter(nœud.fils[c]) ouvCour <- ouvSuiv # on remplace par ouvSuiv la liste qui vient d'être parcourue # fin du constructeur, maintenant "self" est la racine de l'arbre des suffixes de t 3) recherche d'un motif entier rechercher(texte m) n <- self pour i de 1 à m.taille() c <- alphabet[m[i]] n <- fils[c] si n = NIL # motif non trouvé renovoyer -1 renvoyer n.position # remarque : si m.taille() = 0 (chaîne vide), la position renvoyée sera 0.