• Aucun résultat trouvé

INF280 Algorithmes de texte

N/A
N/A
Protected

Academic year: 2022

Partager "INF280 Algorithmes de texte"

Copied!
73
0
0

Texte intégral

(1)

INF280

Algorithmes de texte

Pierre Senellart, Antoine Amarilli

1/28

(2)

Table des matières

Recherche de sous-chaîne

Expressions rationnelles et automates Algorithme de Huffman

Conclusion

2/28

(3)

Trouver les occurrences d’une sous-chaîne dans une chaîne

• Algorithme naïf :

// s est la chaîne, p le motif

for (int i=0, j; i < s.size() - p.size() + 1; ++i) { for (j = 0; j < p.size() && s[i+j] == p[j]; ++j)

;

if (j == p.size())

printf("Match à la position %d\n", i);

}

• En général, similaire à l’implémentation nativedu langage (strstr, fortement optimisée

• ComplexitéO(|s| × |p|)

• Peut-onmieux faire?

3/28

(4)

Knuth–Morris–Pratt : idée

• Motifp,chaînes

• Pour chaquepréfixep0dep, maintenir la taille dupréfixe maximaldepqui est unsuffixe strictdep0

p=“abcababcabd”

00012123450

• Tableau constructible en tempslinéaireen le motifp

4/28

(5)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T i

cnd 5/28

(6)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1

i

cnd 5/28

(7)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1

i

cnd

5/28

(8)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0

i

cnd

5/28

(9)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0

i

cnd

5/28

(10)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0

i

cnd

5/28

(11)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0

i

cnd

5/28

(12)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0

i

cnd

5/28

(13)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0

i

cnd

5/28

(14)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0

i

cnd

5/28

(15)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0

i

cnd

5/28

(16)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0

i

cnd

5/28

(17)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0

i

cnd

5/28

(18)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0

i

cnd

5/28

(19)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1

i

cnd

5/28

(20)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1

i

cnd

5/28

(21)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1

i

cnd

5/28

(22)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2

i

cnd

5/28

(23)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2

i

cnd

5/28

(24)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2

i

cnd

5/28

(25)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2

i

cnd

5/28

(26)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1

i

cnd

5/28

(27)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1

i

cnd

5/28

(28)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1

i

cnd

5/28

(29)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2

i

cnd

5/28

(30)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2

i

cnd

5/28

(31)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2

i

cnd

5/28

(32)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3

i

cnd

5/28

(33)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3

i

cnd

5/28

(34)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3

i

cnd

5/28

(35)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4

i

cnd

5/28

(36)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4

i

cnd

5/28

(37)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4

i

cnd

5/28

(38)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(39)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(40)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(41)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(42)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(43)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5

i

cnd

5/28

(44)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5 0

i

cnd

5/28

(45)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5 0

i

cnd

5/28

(46)

Knuth–Morris–Pratt : calcul du tableau

char p[MAXN]; int T[MAXN+1]; int np = strlen(p);

T[0] = -1;

int cnd = 0;

for (int i = 1; i <= np; i++) { T[i] = cnd;

while (cnd >= 0 && p[cnd] != p[i]) cnd = T[cnd];

cnd++;

}

-1 0 1 2 3 4 5 6 7 8 9 10 11

p a b c a b a b c a b d

T -1 0 0 0 1 2 1 2 3 4 5 0

i

cnd

5/28

(47)

Knuth–Morris–Pratt : interprétation comme automate

T peut être vu comme l’automate déterministedes mots ayant pour suffixep. Par exemple, pourp=abcabd:

0 1 2 3 4 5 6

p a b c a b d

T -1 0 0 0 1 2 0

0 a 1 b 2 c 3 a 4 b 5 d 6

6=a a

6=a,b

6=a,c

6=a

6=a,b

6=a,c,d

c

6=a

a

a a

a

6/28

(48)

Knuth–Morris–Pratt: recherche

char p[MAXN]; int T[MAXN+1];

int np = strlen(p), ns = strlen(s);

[...] // ici, construire la table T comme précédemment int cnd = 0; // position courante dans le motif p for (int i = 0; i <= ns; i++) { // tant qu'on ne lit pas while (cnd >= 0 && p[cnd] != s[i]) // le prochain char de p

cnd = T[cnd]; // on recule dans p

cnd++; // maintenant que le prochain char convient, avancer if (cnd == np) {

// on a atteint la fin de p, donc on a trouvé un match printf("match at %d\n", i - np + 1);

// on recule dans p au cas où le prochain match chevauche cnd = T[cnd];

}

} 7/28

(49)

Recherche d’un mot parmi un ensemble de mots

• DictionnaireDdenmots

• Pour rechercher si un mot est dans cet ensemble :

Vecteur(vector) ouliste chaînée(forward_list) :O(n)

Arbre binaire équilibré(set) :O(logn)

Table de hachage(unordered_set):O(1) maisO(n)dans le pire cas (collisions)

Trie(ou arbre préfixe) :O(1)

8/28

(50)

Trie (arbre préfixe)

i t

e

o n

n n a

t i

in

inn te

tea ten

to

3 12 9

7 5

11

ted d

A

A 15

4

Trie_example.svg, Domaine public, Chris-martin, Wikimedia Commons

• Arbre despréfixesdes mots deD

• Arêtesétiquetéespar des lettres

• On indique sur chaque nœud l’id dansDducheminqui y mène

• Permet de trouver lescontinuations du mot d’entréemdansD, i.e., les mots deDdontmestpréfixe

9/28

(51)

Arbre radix (trie Patricia)

Patricia trie.svg, CC-BY 2.5, Claudio Rocchini, Wikimedia Commons

• Fusionne lesenfants uniques avec leur parent

• Espace mémoireréduit

• Branchement :

• par bit (entiers),

• par lettre,

• par groupe de bits (radix : taille du groupe)

10/28

(52)

Trouver les occurrences d’un ensemble de sous-chaînes dans une chaîne

• DictionnaireDde taillencontenant des mots de taillek

• Chaîne de taillel

• Applications répétées de Knuth–Morris–Pratt ?O(n×(k+l))

• On peut généraliser Knuth–Morris–Pratt à un trie arbitraire (et non une séquence de caractères) :O(n×k+l+m)mest le nombre total d’occurrences (taille du résultat), au plusn×lmais possiblement plus faible

11/28

(53)

Aho–Corasick

a b c

b a c a

b a a

Ahocorasick.svg, CC-BY-SA 3.0, Dllu, Wikimedia Commons

Dictionnaire : a, ab, bab, bc, bca, c, caa.

• Construire untriedu dictionnaire (liens en noir, mots sur fondbleu)

• Ajouter despointeurs(enbleu) vers le plus grand suffixe strict dans le trie

• Ajouter desraccourcis(envert) pour les mots du dictionnaire

Exemple :abccabdonne :

a(parcours normal),

ab(parcours normal),

bc(suivi du pointeurbleu)

puisc(clôture par le pointeurvert),

c(suivi du pointeurbleu),

ca(parcours normal)

puisa(clôture par le pointeurvert),

ab(suivi du pointeurbleu) 12/28

(54)

Aho-Corasick : Construction du trie

int nw; // nombre de mots du dictionnaire char w[MAXW][MAXL]; // contenu des mots du dictionnaire int trie[MAXW*MAXL+2][ALPHA];// trie des mots du dictionnaire int fs; // prochain index libre dans trie int endw[MAXW*MAXL+2];// mot qui se finit à cet index de trie // insérer le mot numéro i de contenu s dans le trie

// en partant du nœud d'index n dans le trie void insert_trie(int i, char *s, int n) {

unsigned char x = s[0];

if (!x) { endw[n] = i; return; } // fin du mot atteinte if (!trie[n][x]) // si pas d'arête étiquetée x depuis n...

trie[n][x] = fs++; // ... on crée un nouveau nœud cible return insert_trie(i, s+1, trie[n][x]);

} 13/28

(55)

Aho-Corasick : Début du code

void aho_corasick() {

// l'index 0 dans le trie indique les nœuds inexistants // la racine du trie est 1

// donc initialement le prochain index libre est 2 fs = 2;

// les mots sont numérotés à partir de 1

// 0 dans endw indique qu'aucun mot ne se termine for (int i = 1; i <= nw; i++)

insert_trie(i, w[i], 1); // insère chaque mot dans le trie [...] // ici, on calcule les pointeurs et les raccourcis }

14/28

(56)

Aho-Corasick : Calcul des pointeurs et raccourcis (1/2)

int pointer[MAXW*MAXL+2]; // pointeurs (en bleu), cf T de KMP int shortcut[MAXW*MAXL+2]; // raccourcis (en vert) queue<int> q;

q.push(1); // explorer en BFS depuis la racine du trie while (!q.empty()) {

int n = q.front();

q.pop();

for (unsigned char x = 0; x < ALPHA; x++) { int n2 = trie[n][x];

if (!n2)

continue; // pas d'arête pour cette lettre depuis n [...] // on traite n2, cf prochain transparent q.push(n2); // continuer le BFS avec n2 }

} 15/28

(57)

Aho-Corasick : Calcul des pointeurs et raccourcis (2/2) // nœud n2 de parent n avec étiquette x, i.e., n -x-> n2 // == calcul du pointeur ==

int p = pointer[n]; // p est le pointeur de n while (p && !trie[p][x]) // tant que p n'a pas d'arête x...

p = pointer[p]; // ... on recule p via le pointeur if (!p) // si p = 0, on est sorti du trie...

pointer[n2] = 1; // ... donc on met pointeur = racine

else // sinon le pointeur est...

pointer[n2] = trie[p][x]; // ... le x-successeur de p // == calcul du raccourci ==

if (endw[pointer[n2]]) // si un mot finit en n2...

shortcut[n2] = pointer[n2]; // ... raccourci = pointeur else // sinon le raccourci est celui du pointeur shortcut[n2] = shortcut[pointer[n2]]; 16/28

(58)

Aho-Corasick : Utilisation

int pos = 1; // position courante dans le trie

char s[MAXS]; int ns = strlen(s); // chaîne à parcourir

for (int i = 0; i < ns; i++) {

unsigned char x = s[i]; // x = nouveau caractère lu while (pos && !trie[pos][x]) // tant que pas d'arête x depuis p...

pos = pointer[pos]; // ... on recule pos suivant pointeur pos = trie[pos][x]; // maintenant on essaie d'avancer par x

if (!pos) // si on est sorti du trie...

pos = 1; // ... on revient à la racine

int posb = pos; // on va énumérer les matches possibles depuis pos...

do { // fin de mot possible en i

if (endw[posb]) // on a vraiment une fin de mot à posb (en i):

printf("match of %s at %d\n", // afficher w[endw[posb]], i - strlen(endw[posb]) + 1); // le match posb = shortcut[posb]; // suivre le raccourci de posb } while (endw[posb]); // ... tant que des mots se terminent

} 17/28

(59)

Arbre des suffixes

3 1

5

A NA

NA NA$

$ $

BANANA$

4 2

0

NA$

$

Suffix tree BANANA.svg, Domaine public, Maciej Jaros and Nils Grimsmo, Wikimedia Commons

• Trie Patricia dessuffixesd’un mot (ici, BANANA, suivi d’un délimiteur $)

• Constructible enO(n2)de droite à gauche

• Constructible enO(n)de gauche à droite, comme les pointeurs d’Aho–Corasick

• Permet d’indexerune chaîne pour rechercher des sous-chaînes

• Nombreusesautres applications: p. ex., plus longue sous-chaîne commune

18/28

(60)

Table des matières

Recherche de sous-chaîne

Expressions rationnelles et automates Algorithme de Huffman

Conclusion

19/28

(61)

Expressions rationnelles

• Langage permettant de décrire desmotifsà rechercher dans une chaîne de caractères

• Par exemple : (a|b)*#(a|b)*(#(a|b|#)*)?

• Généralise la recherche desous-chaînes, de mots d’un dictionnaire, depréfixes, desuffixes, etc.

• Processeurs d’expressions rationnelles dans labibliothèque standardde C++ 2011 (std::regex)

20/28

(62)

Automates

• Expression rationnelleversautomate fini nondéterministe:

algorithme de Thompson: temps linéaire, transitions spontanées

algorithme de Glushkov: temps quadratique, moins d’états

• Reconnaître si une chaîne de longueurnestacceptée par un automate nondéterministe àmétats est enO(n×m)

• Un automate nondéterministe àmétats peut êtretransformé en automate déterministe àO(2m)états en tempsO(2m)

• Reconnaître si une chaîne de longueurnestacceptée par un automate déterministe àmétats est enO(n)

21/28

(63)

Expressions rationnelles et expressions rationnelles

• Les “expressions rationnelles” de C++ incluent desextensions:

Références arrières(indiquer qu’une sous-chaîne se répète)

• Opérateur*gloutonet*?réticent

• Du coup, les implémentations des expressions rationnelles n’utilisent pas des automates, mais dubacktracking

• Souventbeaucoup moins efficaces! Exponentiel en la taille de l’expression rationnelle dans les cas pathologiques

22/28

(64)

Table des matières

Recherche de sous-chaîne

Expressions rationnelles et automates Algorithme de Huffman

Conclusion

23/28

(65)

Algorithme de Huffman : Objectif

• Ensemble desymbolessiavec unpoidswi (e.g., probabilité)

• On veut choisir uncodeCi (mot sur{0,1}) pour chaque mot

• Code préfixe: aucun mot code n’est préfixe d’un autre

• Minimiser lepoids moyen:P

i|Ci| ×wi

Intuition :un motlourd(fréquent), doit avoir un codebref

24/28

(66)

Algorithme de Huffman : Principe

• Représenter uncode préfixecomme unarbre de décision

• Symbolesa,b,c,d,e

25/28

(67)

Algorithme de Huffman : Exemple

A

15

B

7

C

6

D

6

E

5

26/28 https://commons.wikimedia.org/wiki/File:HuffmanCodeAlg.png

(68)

Algorithme de Huffman : Exemple

A

15

B

7

C

6

11

E D

0 1

26/28 https://commons.wikimedia.org/wiki/File:HuffmanCodeAlg.png

(69)

Algorithme de Huffman : Exemple

A

15

13

B C

0 1

11

E D

0 1

26/28 https://commons.wikimedia.org/wiki/File:HuffmanCodeAlg.png

(70)

Algorithme de Huffman : Exemple

A

15

24

D E

0 1

C B

0 1

0 1

26/28 https://commons.wikimedia.org/wiki/File:HuffmanCodeAlg.png

(71)

Algorithme de Huffman : Exemple

39

D E

0 1

B C

0 1

0 1

A

0 1

26/28 https://commons.wikimedia.org/wiki/File:HuffmanCodeAlg.png

(72)

Table des matières

Recherche de sous-chaîne

Expressions rationnelles et automates Algorithme de Huffman

Conclusion

27/28

(73)

En résumé

• Déterminer siune chaîne est dans un ensemble de chaînes:

table de hachage

• Trouver leschaînes du dictionnaire dont une chaîne est préfixe:

trie, arbre radix

• Recherche d’unepetite sous-chaîne dans une chaîne:

implémentation native du langage de programmation

• Recherche d’unelongue sous-chaîne dans une longue chaîne:

Knuth–Morris–Pratt (ou Boyer–Moore, non traité)

• Recherche d’undictionnaire de sous-chaînes dans une chaîne:

Aho–Corasick (indexe les sous-chaînes)

arbre des suffixes (indexe la chaîne)

• Recherche d’unmotif complexe dans une chaîne:

expression rationnelle ou automate compilé à partir du motif

28/28

Références

Documents relatifs

Pour calculer algorithmiquement une expression rationnelle pour chacun d’eux, il suffit donc d’utiliser récursivement le procédé d’élimination basé sur le lemme

[^ac] reconnaît n'importe quel caractère sauf a et c, ou [^0-9] reconnaît n'importe quel caractère qui n'est pas numérique. A l'aide des classes de caractères, cherchez les

3 Algorithmes pour calculer les polynômes de Darboux et les intégrales premières rationnelles de degré borné... , X n ) Vrai pour les polynômes en une variable :..

Écrivez un automate déterministe qui reconnaisse la présence du mot “concom- bre” ou du mot “braconnier” dans un texte, attention, les mots ne sont pas forcément séparés par

Écrivez une expression rationnelle permettant de reconnaître de telles chaînes (il peut y en avoir plusieurs dans le même fichier) : elle devra remplacer les motifs reconnus par

Pour chacune des expressions rationnelles suivantes, construire un automate reconnaissant le lan- gage qu’elles d´enotent :.. (Calcul sur des

[r]

Ce sont plutôt des règlements unilatéraux à portée impersonnelle (visant tous les utilisateurs) qui, associés à la loi des algorithmes, permettent à ces services