On considère l'automate fini déterministe A
qui reconnait tous les mots sur l'alphabet {a,b}
qui ne contiennent pas de b
isolé.
Cet automate
{a,b}
{0,1,2,3}
0
est l'état initial0
et 2
sont les états d'acceptationa | b | |
---|---|---|
0 | 0 | 1 |
1 | 3 | 2 |
2 | 0 | 2 |
3 | 3 | 3 |
Pour sa mise en oeuvre en Java, on utilise la classe Automate
et un programme de test TestAutomate
import java.util.*; public class Automate { /** La fonction de transition de l'automate */ protected int[][] _tableTransitions; /** État initial */ protected int _etatInitial; /** Liste d’états finaux */ protected List<Integer> _etatsFinaux; public Automate(int Initial, Integer[] Acceptants, int[][] _tableTransitions) { this._tableTransitions = _tableTransitions; this._etatInitial = Initial; this._etatsFinaux = Arrays.asList(Acceptants); } /** État interne de l'automate */ protected int etat; /** Positionne l'automate dans son état initial */ protected void etatInitial() { etat = _etatInitial; } /** L'état courant est-il acceptant ? */ protected boolean etatCourantEstAcceptant() { return _etatsFinaux.contains(etat); } /** Étant donné un caractère (donné par son code unicode), déclencher la transition */ protected void etatSuivant(char c) { etat = _tableTransitions[etat][c-'a']; } public boolean reconnait(String mot) { etatInitial(); char[] ch = mot.toCharArray(); for (int i = 0; i < mot.length(); i++) { char c=ch[i]; System.out.println("État : "+ etat + " entrée " + String.format("%c", c)); etatSuivant(c); } System.out.println("État : "+ etat); return etatCourantEstAcceptant(); } }
A
public class TestAutomate { static Automate automate = new Automate( 0, new Integer[] {0,2}, new int[][] {{0,1}, {3,2}, {0,2}, {3,3}} ); public static void main(String args[]) { if (args.length < 1) { System.err.println("Usage : TestAutomate mots ..."); System.exit(1); } for (int i = 0; i < args.length; i++) { String arg = args[i]; boolean r = automate.reconnait(arg); System.out.println("\"" + arg + "\"" + (r ? " est" : " n'est pas") + " reconnu\n"); } } }
A
sur diverses entrées : abbabb
, ababbb
, abbabbaaab
, aaaa
... 6
qui ne soit pas reconnus par A
et deux mots de longueur 6
qui soit reconnus.
À présent, on s'intéresse au problème de recherche de motif dans un texte.
Il s'agit de retrouver toutes les occurrences d’un mot u
dans un texte t
.
On envisage deux algorithmes de recherche basés sur des automates finis.
Σ*u
-- l'ensemble des mots sur Σ
qui se terminent par u
--t
avec cet automate :
l'automate lit le texte t
, caractère par caractère; il détecte une occurrence du mot u
à chaque fois qu'il entre dans un état d'acceptation.bababb
L'AFD qui reconnaît {a,b}*bababb
est décrit par le graphe de transition suivant :
TestAutomate_bababb
pour tester cet automate sur les entrées bababb
, babbababb
(on s'inspirera de la classe TestAutomate
).t=bbabaabababbababbaba
.bababb
dans le texte t
?compte
qui prend en entrée un texte t
et affiche le nombre d'occurences du mot bababb
dans le texte t
.Remarque.
L'automate entre dans l'état i
quand le plus long suffixe de la partie actuellement lue de t
qui est préfixe de u
est de longueur i
.
u
quelconque, construire un AFD qui reconnaît Σ*u
Soit m
la longueur du motif u
, l'AFD qui reconnaît Σ*u
se calcule comme suit :
m+1
états {0,1,..,m}
0
{m}
δ:{0,1,..,m}* Σ → {0,1,..,m}
où δ(i,a)
est définie comme le nombre de lettres du plus grand suffixe u[:i]a
qui soit aussi préfixe de u
.δ(i,a) =
i+1
si a=u[i]
(arc avant)max{j≤ i: u[:j]=u[i-j+1:i]a}
sinon (arc arrière)v
et qui détermine la taille du plus grand suffixe de v
qui soit aussi préfixe du motif u
. AutomateLoc
et écrire le constructeur qui prend le motif u
en argument et construit l'AFD Σ*u
.analyser
qui prend en entrée un texte t
et qui signale la position de toutes les ocurences du mot u
dans t
u
et textes t
.Référence. C. Charras - T. Lecroq : Deterministic Finite Automaton algorithm
On utilise une fenêtre coulisante de la taille du mot u
cherché qui se déplace le long du texte.
Initialement cette fenêtre est au début du texte.
Quand la fenêtre est positionnée en i
,
on détermine le plus long préfixe pref
de u
qui soit suffixe du texte inscrit dans la fenêtre.
pref
est plus petit que u
, on décale la fenêtre au début de pref
(toutes les positions précédentes sont vouées à l'échec);
pref
est u
lui même, une occurrence est détectée et on décale la fenêtre au début du précédent préfixe rencontré.
pref | |||||||||||||||||||||||||||
Le texte | b | a | b | b | a | b | a | a | b | a | b | a | b | b | a | a | a | b | a | b | a | b | b | b | a | b | a |
Fenêtre de | i | à | i+|u|-1 |
Comment déterminer ce préfixe de u
qui soit le plus long
suffixe de la fenêtre lue ?
En cherchant de droite à gauche dans la fenêtre (i.e dans le sens inverse)
les suffixes du mot miroir de u
(i.e. le mot lu en sens
inverse).
Concrétement, on construit l'automate des suffixes du mot miroir de u
(l'AFD qui reconnaît tous les suffixes du mot miroir).
On applique l'automate sur les caractères de la fenêtre lue de
droite à gauche, et à chaque fois que l'automate entre dans un état
final (sans que tous les caractères de la fenêtre n'aient été consommés)
on mémorise la position dans une variable der
.
bababb
bbabab
.
babbabaabababbaaabababbbaba
.
La fenêtre est de longueur m = |bababb| = 6
. On déroule l'automate avec
comme entrée le dernier caractère de la fenêtre, ici b
qui fait passer
l'automate à l'état 1 ; 1 étant final, on mémorise der = 1
. Puis
l'entrée a
pour l'état 7 qui n'est pas final. Et b
pour l'état 8 qui est final, donc der = 3
. Le b
suivant fait passer l'automate dans un état puits et on peut arrêter la
reconnaissance.
Le texte | b | a | b | b | a | b | a | a | b | a | b | a | b | b | a | a | a | b | a | b | a | b | b | b | a | b | a |
États | ⊥ | 8 | 7 | 1 | 0 |
m - der
, soit 3 caractères,
et on recommence.
Le texte | b | a | b | b | a | b | a | a | b | a | b | a | b | b | a | a | a | b | a | b | a | b | b | b | a | b | a |
États | ⊥ | 7 | 1 | 0 |
der
vaut 1, on décale la fenêtre de 5.
Le texte | b | a | b | b | a | b | a | a | b | a | b | a | b | b | a | a | a | b | a | b | a | b | b | b | a | b | a |
États | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
bababb
est trouvé, et comme der
vaut 1, on décale la fenêtre de 5.
Le texte | b | a | b | b | a | b | a | a | b | a | b | a | b | b | a | a | a | b | a | b | a | b | b | b | a | b | a |
États | ⊥ | 5 | 8 | 7 | 0 |
m - 2
…
TestAutomateMiroir
pour tester l'automate des suffixes de miroir(u)=bbabab
.
analyser
, basée sur l'algorithme du miroir, qui prend en argument un texte t
et retourne la liste de toutes les positions initiales du mot u=bababb
recherché dans le texte t
.analyser
sur le texte t=babbabaabababbaaabababbbaba
.Référence. C. Charras - T. Lecroq : Reverse Factor algorithm