{
  "chapter": {
    "id": "recherche-textuelle",
    "level": "terminale",
    "theme": "Algorithmique",
    "title": "Recherche textuelle",
    "description": "Recherche d'un motif dans un texte, algorithme naïf (force\nbrute), algorithme de Boyer-Moore-Horspool (comparaison de\ndroite à gauche, table de décalages), pré-traitement,\ncomplexités, applications (grep, Ctrl+F, ADN).",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Position du problème",
      "statement": "En quoi consiste la **recherche textuelle** ?",
      "options": [
        {
          "text": "Chercher un mot dans un dictionnaire trié",
          "correct": false,
          "feedback": "Erreur : c'est un cas particulier qui se résout par\nrecherche dichotomique. La recherche textuelle est\nplus générale.\n"
        },
        {
          "text": "Compter le nombre de caractères dans un texte",
          "correct": false,
          "feedback": "Erreur : ce n'est pas une recherche, juste un\ncomptage trivial.\n"
        },
        {
          "text": "Trier un texte par ordre alphabétique",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le tri.\n"
        },
        {
          "text": "Trouver toutes les positions d'un motif (chaîne courte) dans un texte (chaîne longue)",
          "correct": true,
          "feedback": "Bonne réponse : c'est exactement le problème étudié.\nApplications : Ctrl+F dans un éditeur, `grep`,\nanalyse de séquences ADN, moteurs de recherche.\n"
        }
      ],
      "explanation": "La recherche textuelle est un problème fondamental en\ninformatique : elle est utilisée des milliards de fois\npar jour (recherche web, éditeurs de texte, analyse de\ndonnées massives)."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "naif-principe"
      ],
      "title": "Principe de l'algorithme naïf",
      "statement": "Quel est le principe de l'**algorithme naïf** (force brute) ?",
      "options": [
        {
          "text": "On utilise une table de hachage pour aller plus vite",
          "correct": false,
          "feedback": "Erreur : c'est l'idée de l'algorithme de\nRabin-Karp, pas du naïf.\n"
        },
        {
          "text": "On teste chaque position de départ possible et on compare le motif caractère par caractère",
          "correct": true,
          "feedback": "Bonne réponse : pour chaque indice `i` de 0 à\n`n - m`, on compare le motif au texte à partir de\nla position `i`. Dès qu'un caractère diffère, on\npasse à la position suivante.\n"
        },
        {
          "text": "On découpe le texte en deux et on cherche dans chaque moitié",
          "correct": false,
          "feedback": "Erreur : ce serait du « diviser pour régner », ce\nque ne fait pas l'algorithme naïf.\n"
        },
        {
          "text": "On compare le motif uniquement à la fin du texte",
          "correct": false,
          "feedback": "Erreur : on teste **toutes** les positions, pas\nseulement la fin.\n"
        }
      ],
      "explanation": "L'algorithme naïf est simple mais inefficace : il\navance toujours d'une seule position après chaque\néchec, même quand le premier caractère ne correspondait\npas."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "naif-borne"
      ],
      "title": "Borne de la boucle",
      "statement": "Dans l'algorithme naïf, la boucle externe va de 0 à\nquelle valeur (incluse) pour pouvoir aligner un motif\nde longueur `m` dans un texte de longueur `n` ?",
      "options": [
        {
          "text": "`n - 1`",
          "correct": false,
          "feedback": "Erreur : on dépasserait la fin du texte. À la\nposition `n - 1`, il ne reste qu'un caractère pour\naligner les `m` caractères du motif.\n"
        },
        {
          "text": "`n - m - 1`",
          "correct": false,
          "feedback": "Erreur : on raterait la dernière position valide\n(le motif placé à la toute fin du texte).\n"
        },
        {
          "text": "`n`",
          "correct": false,
          "feedback": "Erreur : la position `n` n'existe même pas dans le\ntexte.\n"
        },
        {
          "text": "`n - m`",
          "correct": true,
          "feedback": "Bonne réponse : c'est la dernière position où le\nmotif tient encore dans le texte. En Python, on\nécrit donc `range(n - m + 1)` (la borne supérieure\nde `range` étant exclusive).\n"
        }
      ],
      "explanation": "Erreur classique : oublier le `+1` dans `range`. Pour\néviter ce piège, vérifier sur un petit exemple :\n`texte = \"abc\"`, `motif = \"bc\"`, longueurs `n=3`,\n`m=2`. Positions valides : 0, 1. Donc `range(2)` =\n`range(n - m + 1)`."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "naif-complexite"
      ],
      "title": "Complexité du naïf",
      "statement": "Quelle est la complexité dans le **pire cas** de\nl'algorithme naïf de recherche textuelle ?",
      "options": [
        {
          "text": "O(n + m)",
          "correct": false,
          "feedback": "Erreur : cette complexité correspond à\nl'algorithme de Knuth-Morris-Pratt, et non à\nla version naïve.\n"
        },
        {
          "text": "O(n × m)",
          "correct": true,
          "feedback": "Bonne réponse : la boucle externe parcourt environ\nn positions ; pour chacune, on peut faire jusqu'à\nm comparaisons. D'où O(n × m), souvent noté\nO(n²) quand n ≫ m.\n"
        },
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : aucune dichotomie dans cet algorithme.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : O(n) supposerait une seule comparaison\npar position, ce qui n'est pas le cas dans le\npire cas.\n"
        }
      ],
      "explanation": "Le pire cas se rencontre par exemple avec un texte\n`\"aaaa...a\"` et un motif `\"aaab\"` : à chaque position,\nles m-1 premiers caractères correspondent avant\nl'échec, soit ~ n × m comparaisons."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "boyer-moore-principe"
      ],
      "title": "Idée clé de Boyer-Moore",
      "statement": "Quelle est l'**idée centrale** de l'algorithme de\nBoyer-Moore-Horspool ?",
      "options": [
        {
          "text": "Comparer le motif de gauche à droite mais en sautant les voyelles",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec les voyelles. La\ncomparaison va de droite à gauche.\n"
        },
        {
          "text": "Utiliser la programmation dynamique avec mémoïsation",
          "correct": false,
          "feedback": "Erreur : Boyer-Moore n'utilise pas de mémoïsation.\n"
        },
        {
          "text": "Pré-trier le texte pour faire une recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : on ne peut pas trier un texte sans\ndétruire sa structure. La recherche dichotomique\nne s'applique pas ici.\n"
        },
        {
          "text": "Comparer le motif de droite à gauche et sauter plusieurs positions en cas d'échec grâce à une table de décalages",
          "correct": true,
          "feedback": "Bonne réponse : ces deux idées combinées\npermettent d'éviter de comparer chaque caractère\ndu texte. Dans le meilleur cas, on saute m\npositions à chaque fois.\n"
        }
      ],
      "explanation": "Comparer de droite à gauche peut sembler contre-intuitif,\nmais c'est précisément ce qui permet de tirer parti des\ncaractères « rares » dans le motif pour faire de grands\nsauts."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "applications"
      ],
      "title": "Applications concrètes",
      "statement": "Lequel des outils suivants utilise une variante de la\nrecherche textuelle (algorithme naïf ou Boyer-Moore) ?",
      "options": [
        {
          "text": "La commande Unix `grep`",
          "correct": false,
          "feedback": "C'est exact, mais ce n'est pas la **seule** bonne\nréponse.\n"
        },
        {
          "text": "Toutes les réponses précédentes",
          "correct": true,
          "feedback": "Bonne réponse : Ctrl+F, `grep` et la bio-informatique\nsont trois applications classiques. La recherche\ntextuelle est partout.\n"
        },
        {
          "text": "La fonction Ctrl+F d'un éditeur de texte",
          "correct": false,
          "feedback": "C'est exact, mais ce n'est pas la **seule** bonne\nréponse. Lis bien toutes les propositions.\n"
        },
        {
          "text": "L'analyse de séquences ADN",
          "correct": false,
          "feedback": "C'est exact, mais ce n'est pas la **seule** bonne\nréponse.\n"
        }
      ],
      "explanation": "La recherche textuelle est l'une des opérations les\nplus exécutées en informatique. Les implémentations\nréelles utilisent souvent Boyer-Moore ou des variantes\nplus avancées (Aho-Corasick pour plusieurs motifs, par\nexemple)."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "trouve-renvoie"
      ],
      "title": "Que renvoie l'algorithme ?",
      "statement": "Lorsqu'on programme `recherche(texte, motif)`, quel\nest le retour le plus utile ?",
      "options": [
        {
          "text": "Un booléen `True`/`False` indiquant la présence",
          "correct": false,
          "feedback": "Insuffisant : on ne sait pas **où** est le motif,\nni combien de fois il apparaît.\n"
        },
        {
          "text": "La liste des indices de début de chaque occurrence",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'information la plus riche\n(`[]` si absent, `[3, 7]` si présent aux\npositions 3 et 7). Un booléen ou un compte\npeuvent en être déduits.\n"
        },
        {
          "text": "Le texte modifié pour retirer le motif",
          "correct": false,
          "feedback": "Erreur : ce serait un `replace`, pas une recherche.\n"
        },
        {
          "text": "La longueur du motif",
          "correct": false,
          "feedback": "Erreur : c'est connu d'avance via `len(motif)`,\naucun intérêt à la renvoyer.\n"
        }
      ],
      "explanation": "Renvoyer la liste des positions est la convention\nstandard. La méthode Python `str.find()` renvoie\nl'indice de la première occurrence (ou -1) ; pour\ntoutes les occurrences, il faut écrire sa propre\nfonction ou utiliser `re.finditer`."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "naif-trace"
      ],
      "title": "Trace de l'algorithme naïf",
      "statement": "Avec l'algorithme naïf, on cherche `motif = \"ab\"` dans\n`texte = \"babab\"`. Quel est le résultat ?",
      "options": [
        {
          "text": "[0, 2]",
          "correct": false,
          "feedback": "Erreur : à la position 0, on a `\"b\"` ≠ `\"a\"`,\ndonc échec immédiat. Le motif ne commence pas en 0.\n"
        },
        {
          "text": "[1, 3]",
          "correct": true,
          "feedback": "Bonne réponse : positions 1 (`\"ab\"`) et 3 (`\"ab\"`).\nAux positions 0, 2, 4 le premier caractère est\n`\"b\"`, ce qui échoue.\n"
        },
        {
          "text": "[]",
          "correct": false,
          "feedback": "Erreur : le motif `\"ab\"` apparaît bien dans\n`\"babab\"` (deux fois).\n"
        },
        {
          "text": "[1, 2, 3]",
          "correct": false,
          "feedback": "Erreur : à la position 2, le caractère est `\"b\"`,\ndonc échec immédiat (`\"b\"` ≠ `\"a\"`).\n"
        }
      ],
      "explanation": "Pour vérifier rapidement, surligne les caractères du\ntexte un par un et identifie les `\"ab\"` consécutifs."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "pire-cas"
      ],
      "title": "Pire cas du naïf",
      "statement": "Lequel de ces couples (texte, motif) constitue un\n**pire cas** pour l'algorithme naïf ?",
      "options": [
        {
          "text": "texte = `'hello'`, motif = `'hello'`",
          "correct": false,
          "feedback": "Erreur : ce cas se résout en m comparaisons à la\nposition 0. Pas de pire cas.\n"
        },
        {
          "text": "texte = `'aaaaaaaaa'`, motif = `'aaab'`",
          "correct": true,
          "feedback": "Bonne réponse : à chaque position, les 3 premiers\ncaractères correspondent avant l'échec sur le\ndernier. On effectue ~ 4 × n comparaisons.\n"
        },
        {
          "text": "texte = `'abracadabra'`, motif = `'xyz'`",
          "correct": false,
          "feedback": "Au contraire : `'x'` n'apparaît jamais dans le\ntexte, donc l'échec est immédiat (1 comparaison\npar position). C'est un **bon** cas pour le naïf.\n"
        },
        {
          "text": "texte = `'a'`, motif = `'ab'`",
          "correct": false,
          "feedback": "Erreur : `m > n`, la boucle ne s'exécute même pas.\n"
        }
      ],
      "explanation": "Le pire cas se rencontre quand le motif et le texte\npartagent un long préfixe commun à chaque position\n(ex. : alphabets très réduits, motifs « presque\nidentiques » au texte)."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "direction"
      ],
      "title": "Direction de comparaison",
      "statement": "Dans Boyer-Moore-Horspool, par quel caractère du motif\ncommence-t-on la comparaison ?",
      "options": [
        {
          "text": "Le premier (gauche)",
          "correct": false,
          "feedback": "Erreur : ce serait l'algorithme naïf. Boyer-Moore\nfait l'inverse.\n"
        },
        {
          "text": "Le dernier (droite)",
          "correct": true,
          "feedback": "Bonne réponse : on aligne le motif sur le texte\npuis on compare d'abord `motif[m-1]` avec\n`texte[i + m - 1]`. C'est ce caractère qui décide\ndu décalage à appliquer en cas d'échec.\n"
        },
        {
          "text": "Un caractère choisi au hasard",
          "correct": false,
          "feedback": "Erreur : la recherche serait non déterministe.\n"
        },
        {
          "text": "Le caractère du milieu",
          "correct": false,
          "feedback": "Erreur : aucun algorithme classique ne fait cela.\n"
        }
      ],
      "explanation": "Comparer de droite à gauche est la clé : si le\ncaractère du texte aligné avec la fin du motif n'est\npas dans le motif, on peut sauter de m positions sans\nrisque."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "pre-traitement"
      ],
      "title": "Construction de la table",
      "statement": "Pour le motif `\"dab\"` (longueur 3), quelle est la table\nde décalages de Boyer-Moore-Horspool ?",
      "options": [
        {
          "text": "{'d': 3, 'a': 3, 'b': 3}",
          "correct": false,
          "feedback": "Erreur : la valeur 3 (= m) correspond aux\ncaractères **absents** du motif.\n"
        },
        {
          "text": "{'d': 2, 'a': 1}",
          "correct": true,
          "feedback": "Bonne réponse : pour `'d'` en position 0,\ndécalage = 3 - 1 - 0 = 2 ; pour `'a'` en\nposition 1, décalage = 3 - 1 - 1 = 1. Le `'b'`\nfinal est exclu.\n"
        },
        {
          "text": "{'d': 0, 'a': 1, 'b': 2}",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse, et il ne faut pas\ninclure `'b'`.\n"
        },
        {
          "text": "{'d': 2, 'a': 1, 'b': 0}",
          "correct": false,
          "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'b'`) dans la table, sinon on risque un\ndécalage de 0 qui ferait boucler l'algorithme.\n"
        }
      ],
      "explanation": "Formule : pour le caractère c en position i (la plus\nà droite, hors dernière position), décalage = m - 1 - i.\nCaractères absents du motif : décalage = m."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "pre-traitement"
      ],
      "title": "Caractère absent du motif",
      "statement": "Avec le motif `\"dab\"` (longueur 3), le caractère du\ntexte aligné avec la fin du motif est `'z'`. Quel\ndécalage applique-t-on ?",
      "options": [
        {
          "text": "1",
          "correct": false,
          "feedback": "Erreur : c'est le décalage **par défaut** de\nl'algorithme naïf, mais Boyer-Moore fait mieux.\n"
        },
        {
          "text": "3 (la longueur du motif)",
          "correct": true,
          "feedback": "Bonne réponse : `'z'` n'apparaît pas dans `\"dab\"`,\non peut donc sauter de toute la longueur du\nmotif (3) sans risquer de manquer une occurrence.\n"
        },
        {
          "text": "0",
          "correct": false,
          "feedback": "Erreur : un décalage de 0 ferait boucler\nl'algorithme indéfiniment.\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : 2 est le décalage pour `'d'`, pas pour\nun caractère absent.\n"
        }
      ],
      "explanation": "C'est le « cadeau » de Boyer-Moore : un grand alphabet\n(lettres, chiffres, ponctuation) signifie beaucoup de\ncaractères absents du motif, donc des sauts maximaux."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "pre-traitement-doublon"
      ],
      "title": "Caractère répété",
      "statement": "Pour le motif `\"maman\"` (longueur 5), quelle est la\ntable de décalages ?",
      "options": [
        {
          "text": "{'m': 0, 'a': 3}",
          "correct": false,
          "feedback": "Erreur : `'m'` apparaît aussi en position 0, on\nne garde que la position **la plus à droite**\n(hors dernière position).\n"
        },
        {
          "text": "{'m': 4, 'a': 1}",
          "correct": false,
          "feedback": "Erreur : `'m'` final est exclu mais celui de\nposition 2 donne 5 - 1 - 2 = 2, pas 4.\n"
        },
        {
          "text": "{'m': 2, 'a': 1}",
          "correct": true,
          "feedback": "Bonne réponse : `'m'` en position 2 (la plus à\ndroite hors fin), décalage 5 - 1 - 2 = 2 ;\n`'a'` en position 3, décalage 5 - 1 - 3 = 1.\n"
        },
        {
          "text": "{'m': 2, 'a': 3, 'n': 0}",
          "correct": false,
          "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'n'`).\n"
        }
      ],
      "explanation": "Quand un caractère apparaît plusieurs fois dans le\nmotif, on garde la dernière occurrence (avant la fin)\ncar elle donne le **plus petit** décalage sûr."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "pourquoi-exclure-fin"
      ],
      "title": "Pourquoi exclure le dernier caractère ?",
      "statement": "Pourquoi exclut-on le dernier caractère du motif lors\ndu pré-traitement ?",
      "options": [
        {
          "text": "Parce qu'il donnerait un décalage de 0, ce qui ferait boucler l'algorithme",
          "correct": true,
          "feedback": "Bonne réponse : en cas d'égalité avec le dernier\ncaractère du motif, on veut chercher la\nprécédente occurrence (à gauche), ou décaler de\nm s'il n'y en a pas. Décaler de 0 ferait revenir\nà la même position et boucler.\n"
        },
        {
          "text": "Pour économiser de la mémoire",
          "correct": false,
          "feedback": "Erreur : le gain mémoire est négligeable. La\nvraie raison est algorithmique.\n"
        },
        {
          "text": "Parce qu'il est toujours absent du motif",
          "correct": false,
          "feedback": "Erreur : il fait évidemment partie du motif\n(puisqu'il en est le dernier caractère).\n"
        },
        {
          "text": "Parce que Python ne permet pas d'indexer la dernière position",
          "correct": false,
          "feedback": "Erreur : Python le permet sans problème.\n"
        }
      ],
      "explanation": "C'est un point subtil mais essentiel. Sans cette\nexclusion, l'algorithme entrerait dans une boucle\ninfinie dès qu'on rencontre un caractère identique au\ndernier du motif."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "boyer-moore-trace"
      ],
      "title": "Trace de Boyer-Moore",
      "statement": "On cherche `motif = \"dab\"` dans `texte = \"abracadabra\"`.\nAu premier alignement (i=0), on compare `texte[2]`\n(`'r'`) avec `motif[2]` (`'b'`). Échec. Sachant que\n`'r'` n'est pas dans le motif, de combien décale-t-on ?",
      "options": [
        {
          "text": "11 (la longueur du texte)",
          "correct": false,
          "feedback": "Erreur : on n'a aucune raison de sauter à la fin\ndu texte.\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : 2 est le décalage de `'d'`, pas d'un\ncaractère absent.\n"
        },
        {
          "text": "1",
          "correct": false,
          "feedback": "Erreur : ce serait l'algorithme naïf. Boyer-Moore\ntire parti de l'absence de `'r'` dans le motif.\n"
        },
        {
          "text": "3 (la longueur du motif)",
          "correct": true,
          "feedback": "Bonne réponse : `'r'` est absent de `\"dab\"`, on\nsaute de m = 3 positions. La nouvelle position\ndevient i = 3.\n"
        }
      ],
      "explanation": "C'est précisément ce type de saut qui fait la\npuissance de Boyer-Moore : sur du texte naturel\n(alphabet riche), de tels sauts maximaux sont\nfréquents."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "boyer-moore-complexite"
      ],
      "title": "Complexité dans le pire cas",
      "statement": "Quelle est la complexité de Boyer-Moore-Horspool dans\nle **pire cas** ?",
      "options": [
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : O(n) est atteignable mais pas garanti\ndans le pire cas (la version Horspool n'a pas\ncette garantie).\n"
        },
        {
          "text": "O(log n)",
          "correct": false,
          "feedback": "Erreur : aucune dichotomie.\n"
        },
        {
          "text": "O(n × m)",
          "correct": true,
          "feedback": "Bonne réponse : dans le pire cas (par exemple,\nmotif `\"aaab\"` dans `\"aaaa...a\"`), la complexité\nrejoint celle du naïf. La force de Boyer-Moore\nest dans le cas moyen et le meilleur cas.\n"
        },
        {
          "text": "O(n + m)",
          "correct": false,
          "feedback": "Erreur : cette complexité correspond à\nl'algorithme de Knuth-Morris-Pratt, et non à\ncelui de Boyer-Moore-Horspool.\n"
        }
      ],
      "explanation": "Point important : l'algorithme de\nBoyer-Moore-Horspool n'apporte aucune garantie\nde complexité dans le pire cas. C'est en moyenne\net au meilleur cas qu'il se distingue.\nL'algorithme de Knuth-Morris-Pratt, en revanche,\ngarantit une complexité en $O(n + m)$, mais avec\nun prétraitement plus coûteux."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "meilleur-cas"
      ],
      "title": "Meilleur cas sous-linéaire",
      "statement": "Quelle est la complexité dans le **meilleur cas** de\nBoyer-Moore-Horspool ?",
      "options": [
        {
          "text": "O(n / m)",
          "correct": true,
          "feedback": "Bonne réponse : si à chaque échec on saute de m\npositions, on n'effectue qu'environ n/m\ncomparaisons. C'est **sous-linéaire** : on trouve\nle motif sans examiner chaque caractère du texte.\n"
        },
        {
          "text": "O(n)",
          "correct": false,
          "feedback": "Erreur : on fait mieux que linéaire dans le\nmeilleur cas. Boyer-Moore peut être sous-linéaire.\n"
        },
        {
          "text": "O(m)",
          "correct": false,
          "feedback": "Erreur : il faut au minimum traverser le texte\n(modulo les sauts), donc cela dépend de n.\n"
        },
        {
          "text": "O(1)",
          "correct": false,
          "feedback": "Erreur : on a toujours besoin de plusieurs\ncomparaisons.\n"
        }
      ],
      "explanation": "Cette propriété sous-linéaire est ce qui rend\nBoyer-Moore particulièrement performant pour les\nmotifs longs avec un alphabet varié."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "pas-de-gain"
      ],
      "title": "Quand BM n'apporte rien",
      "statement": "Dans quel cas Boyer-Moore-Horspool n'apporte-t-il\n**aucun gain** par rapport à l'algorithme naïf ?",
      "options": [
        {
          "text": "Quand l'alphabet est large",
          "correct": false,
          "feedback": "Erreur : c'est le cas **favorable** à\nBoyer-Moore (sauts maximaux fréquents).\n"
        },
        {
          "text": "Quand le texte et le motif ne contiennent presque que des caractères identiques (par exemple uniquement des `'a'`)",
          "correct": true,
          "feedback": "Bonne réponse : avec un alphabet réduit, les\ndécalages sont presque toujours de 1 et on\nretombe sur un parcours position par position.\n"
        },
        {
          "text": "Quand le motif est très court",
          "correct": false,
          "feedback": "Partiellement vrai (les sauts sont limités à m),\nmais ce n'est pas le **pire cas**.\n"
        },
        {
          "text": "Quand le texte est très long",
          "correct": false,
          "feedback": "Erreur : au contraire, plus le texte est long,\nplus l'écart en valeur absolue grandit.\n"
        }
      ],
      "explanation": "Règle empirique : Boyer-Moore est efficace si motif\nlong + alphabet varié. Les pires cas se rencontrent\navec des séquences quasi-uniformes (séquences ADN\navec un seul nucléotide, par exemple)."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "chaine-vide"
      ],
      "title": "Cas particuliers",
      "statement": "Que doit renvoyer `recherche_naif(\"abc\", \"\")` (motif\nvide) selon la convention de Python ?",
      "options": [
        {
          "text": "[]",
          "correct": false,
          "feedback": "Discutable, mais la convention Python (`str.find`,\n`re.finditer`) considère le motif vide comme\nprésent à toutes les positions.\n"
        },
        {
          "text": "[0, 1, 2, 3]",
          "correct": true,
          "feedback": "Bonne réponse : par convention, le motif vide est\nprésent à chaque position du texte, y compris à\nla fin. Pour un texte de longueur 3, cela donne\nn + 1 = 4 positions.\n"
        },
        {
          "text": "[0]",
          "correct": false,
          "feedback": "Erreur : pas seulement à la position 0.\n"
        },
        {
          "text": "Une erreur (`ValueError`)",
          "correct": false,
          "feedback": "Erreur : Python ne lève pas d'erreur pour le\nmotif vide.\n"
        }
      ],
      "explanation": "Cas piège classique. Beaucoup d'implémentations\ndoivent traiter ce cas explicitement (par exemple en\najoutant `if m == 0: return list(range(n + 1))`)."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "grep-applications"
      ],
      "title": "Mécanisme de `grep`",
      "statement": "La commande Unix `grep` (et ses variantes `egrep`,\n`fgrep`) utilise principalement quel type d'algorithme\npour la recherche de motifs simples ?",
      "options": [
        {
          "text": "Algorithme naïf O(n × m)",
          "correct": false,
          "feedback": "Erreur : `grep` est très optimisé. Le naïf est\ntrop lent pour des fichiers volumineux.\n"
        },
        {
          "text": "Programmation dynamique",
          "correct": false,
          "feedback": "Erreur : la programmation dynamique sert pour\nd'autres problèmes (alignement de séquences,\ndistance d'édition).\n"
        },
        {
          "text": "Recherche dichotomique sur le texte",
          "correct": false,
          "feedback": "Erreur : impossible sans pré-trier le texte.\n"
        },
        {
          "text": "Variantes de Boyer-Moore (avec `fgrep` ou les motifs sans expression régulière)",
          "correct": true,
          "feedback": "Bonne réponse : `fgrep` (ou `grep -F`) utilise\ndes variantes de Boyer-Moore pour les motifs\nfixes. Pour les expressions régulières, `grep`\nutilise des automates finis (DFA/NFA).\n"
        }
      ],
      "explanation": "`grep` est un excellent exemple d'application directe\ndes algorithmes étudiés en NSI. Sa rapidité tient à\nl'utilisation conjointe de Boyer-Moore et d'automates\ncompilés."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "trace-complete"
      ],
      "title": "Nombre d'alignements de BM",
      "statement": "On cherche `motif = \"dab\"` (longueur 3) dans\n`texte = \"abracadabra\"` (longueur 11) avec\nBoyer-Moore-Horspool. Combien d'alignements distincts\ndu motif sont effectués (i.e. valeurs successives de\n`i`) avant la fin de l'algorithme ?",
      "options": [
        {
          "text": "11",
          "correct": false,
          "feedback": "Erreur : on ne teste pas chaque position du\ntexte (c'est tout l'intérêt de Boyer-Moore).\n"
        },
        {
          "text": "2",
          "correct": false,
          "feedback": "Erreur : trop peu, on doit bien tester plusieurs\npositions intermédiaires.\n"
        },
        {
          "text": "9 (comme l'algorithme naïf)",
          "correct": false,
          "feedback": "Erreur : Boyer-Moore fait moins d'alignements\ngrâce aux sauts.\n"
        },
        {
          "text": "4",
          "correct": true,
          "feedback": "Bonne réponse : i = 0 → saute de 3 → i = 3 →\nsaute de 1 → i = 4 → saute de 2 → i = 6 (motif\ntrouvé) → i = 7 → fin. Soit 4 alignements\ndistincts (0, 3, 4, 6) avant détection.\n"
        }
      ],
      "explanation": "Trace complète : i=0, échec sur 'r' (saute 3) ; i=3,\néchec sur 'a' (saute 1) ; i=4, échec sur 'd' (saute\n2) ; i=6, succès. Quatre alignements pour 9 dans le\nnaïf : économie de plus de la moitié."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "pre-traitement-difficile"
      ],
      "title": "Table d'un motif complexe",
      "statement": "Quelle est la table de décalages de\nBoyer-Moore-Horspool pour le motif `\"abracadabra\"`\n(longueur 11) ?",
      "options": [
        {
          "text": "{'a': 1, 'b': 2, 'r': 3, 'c': 4, 'd': 5}",
          "correct": false,
          "feedback": "Erreur : valeurs incorrectes. Il faut prendre la\ndernière position (hors fin) de chaque\ncaractère.\n"
        },
        {
          "text": "{'a': 3, 'b': 2, 'r': 1, 'c': 6, 'd': 4}",
          "correct": true,
          "feedback": "Bonne réponse. Dernière position (hors fin) de\nchaque caractère : a en 7 → 11-1-7=3 ; b en 8 →\n11-1-8=2 ; r en 9 → 11-1-9=1 ; c en 4 → 11-1-4=6 ;\nd en 6 → 11-1-6=4. Le `'a'` final est exclu.\n"
        },
        {
          "text": "{'a': 0, 'b': 1, 'r': 2, 'c': 3, 'd': 4}",
          "correct": false,
          "feedback": "Erreur : ce sont les positions, pas les\ndécalages. Le décalage est m - 1 - position.\n"
        },
        {
          "text": "{'a': 3, 'b': 2, 'r': 1, 'c': 6, 'd': 4, 'a': 0}",
          "correct": false,
          "feedback": "Erreur : le `'a'` final est **exclu** ; on n'a\nqu'une seule entrée pour `'a'` (la position 7).\n"
        }
      ],
      "explanation": "Méthode systématique : balayer le motif de droite à\ngauche en s'arrêtant **avant** le dernier caractère.\nPour chaque nouveau caractère rencontré, calculer\nm - 1 - i."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "comparaison-complexites"
      ],
      "title": "Choix d'algorithme",
      "statement": "On veut chercher un motif de longueur 100 dans un\ntexte ADN (alphabet `{A, C, G, T}`) de 10⁹\nnucléotides. Quel algorithme est le **plus pertinent** ?",
      "options": [
        {
          "text": "Un algorithme spécialisé (Knuth-Morris-Pratt, Aho-Corasick, ou une indexation par transformée de Burrows-Wheeler) garantissant une complexité en $O(n + m)$",
          "correct": true,
          "feedback": "Pour des séquences ADN volumineuses, on\nutilise l'algorithme de Knuth-Morris-Pratt,\ncelui d'Aho-Corasick (pour plusieurs motifs\nrecherchés simultanément), ou des\nindexations précalculées (les outils BWA et\nBowtie reposent sur la transformée de\nBurrows-Wheeler).\n"
        },
        {
          "text": "Algorithme naïf, suffisant pour la plupart des cas",
          "correct": false,
          "feedback": "Erreur : avec n = 10⁹ et m = 100, le pire cas\nserait 10¹¹ comparaisons. Trop lent.\n"
        },
        {
          "text": "Recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : impossible sans tri du texte (ce qui\ndétruirait la séquence).\n"
        },
        {
          "text": "Boyer-Moore-Horspool, simple à implémenter mais limité par l'alphabet réduit",
          "correct": false,
          "feedback": "C'est mieux que le naïf mais l'alphabet à 4\nsymboles limite les sauts. Il existe mieux.\n"
        }
      ],
      "explanation": "Le programme de NSI s'arrête à Boyer-Moore-Horspool,\nmais en bio-informatique réelle, on utilise des\nstructures de données plus avancées (arbres de\nsuffixes, FM-index) pour gérer des génomes entiers."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "debug"
      ],
      "title": "Bug subtil",
      "statement": "Un élève écrit la boucle externe de Boyer-Moore comme\n`while i < n - m:` au lieu de `while i <= n - m:`.\nQuel est l'effet ?",
      "options": [
        {
          "text": "L'algorithme entre dans une boucle infinie",
          "correct": false,
          "feedback": "Erreur : la boucle se termine bien, juste trop\ntôt.\n"
        },
        {
          "text": "L'algorithme est plus rapide (gain négligeable)",
          "correct": false,
          "feedback": "Erreur : la performance n'est pas le problème.\n"
        },
        {
          "text": "L'algorithme renvoie des positions incorrectes",
          "correct": false,
          "feedback": "Erreur : les positions trouvées sont correctes,\non en rate juste certaines (celles à la fin).\n"
        },
        {
          "text": "L'algorithme rate les motifs situés à la toute fin du texte",
          "correct": true,
          "feedback": "Bonne réponse : la position `i = n - m` est la\ndernière où le motif tient encore dans le texte.\nLa condition `i < n - m` exclut cette position\net rate les occurrences finales.\n"
        }
      ],
      "explanation": "Bug très classique des problèmes de bornes :\n`<` versus `<=`. Un test sur `texte = \"abc\"`,\n`motif = \"bc\"` détecte immédiatement le bug : `n - m`\nvaut 1, donc `while i < 1` ne teste que i=0 et rate\nle motif en position 1."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Comparaison synthétique",
      "statement": "Parmi les affirmations suivantes sur les algorithmes\nde recherche textuelle vus en NSI, laquelle est\n**fausse** ?",
      "options": [
        {
          "text": "L'algorithme naïf a une complexité O(n × m) dans le pire cas",
          "correct": false,
          "feedback": "Vrai : c'est sa complexité de pire cas standard.\n"
        },
        {
          "text": "Boyer-Moore-Horspool peut atteindre O(n / m) dans le meilleur cas",
          "correct": false,
          "feedback": "Vrai : c'est sa propriété sous-linéaire\nremarquable.\n"
        },
        {
          "text": "Pour de nombreux motifs simultanés, on préfère Aho-Corasick à Boyer-Moore",
          "correct": false,
          "feedback": "Vrai : Aho-Corasick traite plusieurs motifs en\nun seul passage du texte (utilisé par les\nantivirus, par exemple).\n"
        },
        {
          "text": "Boyer-Moore-Horspool est toujours plus rapide que l'algorithme naïf",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : sur certains\ntextes-pathologiques (alphabet réduit, motif\nrépétitif), Boyer-Moore retombe au pire cas du\nnaïf, voire est **plus lent** à cause de la\ngestion des décalages.\n"
        }
      ],
      "explanation": "« Toujours plus rapide » est presque toujours faux en\nalgorithmique : chaque algorithme a ses cas\nfavorables et ses pathologies. Le choix dépend du\nprofil des données."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "code-naif"
      ],
      "title": "Code de l'algorithme naïf",
      "statement": "Quelle fonction Python implémente correctement\nl'algorithme naïf de recherche textuelle, en\nrenvoyant la liste des indices de début de chaque\noccurrence ?",
      "options": [
        {
          "text": "```\ndef recherche_naif(texte, motif):\n    for i in range(len(texte)):\n        if texte[i] == motif:\n            return i\n    return -1\n```\n",
          "correct": false,
          "feedback": "Erreur : on compare un seul caractère du texte\navec **toute la chaîne** motif, ce qui est\ntoujours faux (sauf si le motif est de\nlongueur 1). Et on ne renvoie qu'une seule\nposition au lieu de toutes les occurrences.\n"
        },
        {
          "text": "```\ndef recherche_naif(texte, motif):\n    positions = []\n    n, m = len(texte), len(motif)\n    for i in range(n - m + 1):\n        j = 0\n        while j < m and texte[i + j] == motif[j]:\n            j += 1\n        if j == m:\n            positions.append(i)\n    return positions\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : la boucle externe parcourt les\npositions valides ; la boucle interne compare\ncaractère par caractère et s'arrête au premier\néchec. Si on a comparé tous les caractères du\nmotif (`j == m`), on a trouvé une occurrence.\n"
        },
        {
          "text": "```\ndef recherche_naif(texte, motif):\n    return [i for i, c in enumerate(texte) if c == motif[0]]\n```\n",
          "correct": false,
          "feedback": "Erreur : on ne vérifie que le premier\ncaractère, pas le motif complet. Pour\n`motif = \"abc\"` dans `\"abracadabra\"`, on\nrenverrait toutes les positions de `'a'`,\ny compris celles où `\"abc\"` n'est pas le\ndébut.\n"
        },
        {
          "text": "```\ndef recherche_naif(texte, motif):\n    positions = []\n    for i in range(len(texte)):\n        if texte[i:i + len(motif)] == motif:\n            positions.append(i)\n    return positions\n```\n",
          "correct": false,
          "feedback": "Cette version fonctionne, mais utilise une\ncomparaison de tranches qui repose sur\nl'opération `==` de Python. Elle masque la\nmécanique caractère par caractère qu'on\nsouhaite illustrer dans l'algorithme naïf\npédagogique. De plus, la boucle va trop loin\n(jusqu'à `len(texte) - 1`), ce qui peut\nprovoquer des comparaisons inutiles avec des\ntranches plus courtes que `m`.\n"
        }
      ],
      "explanation": "Cet algorithme illustre clairement le schéma de\ndouble boucle (externe sur les positions, interne\nsur la comparaison caractère par caractère).\nC'est la base pédagogique avant d'aborder\nBoyer-Moore."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "boyer-moore-trace-detaillee"
      ],
      "title": "Saut maximal en pratique",
      "statement": "Avec le motif `motif = \"exercice\"` (longueur 8) et\nle texte `\"l'élève fait son devoir\"`, on aligne le\nmotif au tout début. Le caractère du texte aligné\navec la fin du motif est `'a'` (de « fait »).\nSachant que `'a'` n'apparaît pas dans `\"exercice\"`,\nde combien décale-t-on ?",
      "options": [
        {
          "text": "1 position",
          "correct": false,
          "feedback": "Erreur : c'est ce que ferait l'algorithme naïf\n(avancer d'une position après chaque échec).\nL'intérêt de Boyer-Moore est précisément de\nsauter davantage en s'appuyant sur la table\nde décalages.\n"
        },
        {
          "text": "0 position (on doit tester à nouveau au même endroit)",
          "correct": false,
          "feedback": "Erreur : un décalage de 0 ferait boucler\nl'algorithme indéfiniment. C'est précisément\npour cela qu'on exclut le dernier caractère\ndu motif lors du pré-traitement de la table\nde décalages.\n"
        },
        {
          "text": "4 positions (la moitié de la longueur du motif)",
          "correct": false,
          "feedback": "Erreur : Boyer-Moore ne fait pas de demi-saut\narbitraire. La règle est claire : si le\ncaractère est absent du motif, on saute de\nla longueur entière `m` du motif.\n"
        },
        {
          "text": "8 positions (la longueur du motif)",
          "correct": true,
          "feedback": "Bonne réponse : `'a'` n'étant pas présent dans\n`\"exercice\"`, on peut sauter de toute la\nlongueur du motif sans risque de manquer une\noccurrence. C'est le saut maximal possible.\nSur du texte courant, ce type de saut est\nfréquent et explique la rapidité de\nBoyer-Moore.\n"
        }
      ],
      "explanation": "C'est ce mécanisme de saut maximal qui rend\nBoyer-Moore sous-linéaire dans le meilleur cas :\npour des textes en alphabet riche (lettres latines,\nponctuation), la plupart des caractères du texte\nsont absents du motif, et on saute donc presque\ntoujours de `m` positions."
    }
  ]
}