{
  "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).",
    "parties": [
      {
        "id": "naif",
        "title": "Recherche naïve"
      },
      {
        "id": "boyer-moore",
        "title": "Algorithme de Boyer-Moore"
      },
      {
        "id": "analyse",
        "title": "Complexité et cas particuliers"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Position du problème",
      "partie": "naif",
      "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 entièrement un texte par ordre alphabétique",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le tri.\n"
        },
        {
          "text": "Trouver toutes les positions d'un motif dans un texte",
          "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",
      "partie": "naif",
      "statement": "Quel est le principe de l'**algorithme naïf** (force brute) ?",
      "options": [
        {
          "text": "On compare depuis la fin du motif et on saute plusieurs positions",
          "correct": false,
          "feedback": "Erreur : c'est l'idée de l'algorithme de Boyer-Moore. L'algorithme naïf compare de gauche à droite et n'avance que d'une position après chaque échec.\n"
        },
        {
          "text": "On teste chaque position de départ et on compare 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",
      "partie": "naif",
      "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",
      "partie": "naif",
      "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 : un coût en O(n + m) supposerait de ne jamais revenir sur les caractères déjà comparés. L'algorithme naïf peut refaire jusqu'à m comparaisons à chacune des positions du texte.\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",
      "partie": "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 une mémoïsation complète",
          "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 en cas d'échec via 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",
      "partie": "analyse",
      "statement": "Lequel des outils suivants utilise une variante de la\nrecherche textuelle (algorithme naïf ou Boyer-Moore) ?",
      "options": [
        {
          "text": "La commande Unix `grep`, et aucun autre outil",
          "correct": false,
          "feedback": "`grep` utilise bien la recherche textuelle, mais ce\nn'est pas le **seul** outil concerné : Ctrl+F et\nl'analyse de séquences ADN aussi.\n"
        },
        {
          "text": "`grep`, Ctrl+F et l'analyse de séquences ADN",
          "correct": true,
          "feedback": "Bonne réponse : Ctrl+F, `grep` et la bio-informatique\nsont trois applications classiques, qui reposent\ntoutes sur la recherche d'un motif dans un texte. La\nrecherche textuelle est partout.\n"
        },
        {
          "text": "La fonction Ctrl+F d'un éditeur de texte, et elle seule",
          "correct": false,
          "feedback": "Ctrl+F utilise bien la recherche textuelle, mais ce\nn'est pas la **seule** application : `grep` et\nl'analyse de séquences ADN aussi. Lis bien toutes les\npropositions.\n"
        },
        {
          "text": "L'analyse de séquences ADN exclusivement",
          "correct": false,
          "feedback": "L'analyse de séquences ADN utilise bien la recherche\ntextuelle, mais ce n'est pas la **seule** application :\n`grep` et Ctrl+F aussi.\n"
        }
      ],
      "explanation": "La recherche textuelle est l'une des opérations les plus exécutées en informatique. Les implémentations réelles utilisent souvent Boyer-Moore ou des variantes de cet algorithme."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "trouve-renvoie"
      ],
      "title": "Que renvoie l'algorithme ?",
      "partie": "naif",
      "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",
      "partie": "naif",
      "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",
      "partie": "analyse",
      "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",
      "partie": "naif",
      "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": [
        "boyer-moore",
        "table"
      ],
      "title": "Construction de la table",
      "partie": "boyer-moore",
      "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, décalage = 3 - 1 - 0 = 2 ; pour `'a'` en position 1, décalage = 3 - 1 - 1 = 1. Le `'b'`\nfinal est exclu.\n"
        },
        {
          "text": "`{'d': 0, 'a': 1, 'b': 2}`",
          "correct": false,
          "feedback": "Erreur : ce sont les positions, pas les décalages, 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.",
      "variants": [
        {
          "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, décalage = 3 - 1 - 0 = 2 ; pour `'a'` en position 1, décalage = 3 - 1 - 1 = 1. Le `'b'`\nfinal est exclu.\n"
            },
            {
              "text": "`{'d': 0, 'a': 1, 'b': 2}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les décalages, 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."
        },
        {
          "statement": "Pour le motif `\"code\"` (longueur 4), quelle est la table\nde décalages de Boyer-Moore-Horspool ?",
          "options": [
            {
              "text": "`{'c': 4, 'o': 4, 'd': 4, 'e': 4}`",
              "correct": false,
              "feedback": "Erreur : la valeur 4 (= m) correspond aux\ncaractères **absents** du motif.\n"
            },
            {
              "text": "`{'c': 3, 'o': 2, 'd': 1}`",
              "correct": true,
              "feedback": "Bonne réponse : pour `'c'` en position 0, décalage = 4 - 1 - 0 = 3 ; pour `'o'` en position 1, décalage = 4 - 1 - 1 = 2 ; pour `'d'` en position 2, décalage = 4 - 1 - 2 = 1. Le `'e'`\nfinal est exclu.\n"
            },
            {
              "text": "`{'c': 0, 'o': 1, 'd': 2, 'e': 3}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les décalages, et il ne faut pas\ninclure `'e'`.\n"
            },
            {
              "text": "`{'c': 3, 'o': 2, 'd': 1, 'e': 0}`",
              "correct": false,
              "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'e'`) 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."
        },
        {
          "statement": "Pour le motif `\"tram\"` (longueur 4), quelle est la table\nde décalages de Boyer-Moore-Horspool ?",
          "options": [
            {
              "text": "`{'t': 4, 'r': 4, 'a': 4, 'm': 4}`",
              "correct": false,
              "feedback": "Erreur : la valeur 4 (= m) correspond aux\ncaractères **absents** du motif.\n"
            },
            {
              "text": "`{'t': 3, 'r': 2, 'a': 1}`",
              "correct": true,
              "feedback": "Bonne réponse : pour `'t'` en position 0, décalage = 4 - 1 - 0 = 3 ; pour `'r'` en position 1, décalage = 4 - 1 - 1 = 2 ; pour `'a'` en position 2, décalage = 4 - 1 - 2 = 1. Le `'m'`\nfinal est exclu.\n"
            },
            {
              "text": "`{'t': 0, 'r': 1, 'a': 2, 'm': 3}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les décalages, et il ne faut pas\ninclure `'m'`.\n"
            },
            {
              "text": "`{'t': 3, 'r': 2, 'a': 1, 'm': 0}`",
              "correct": false,
              "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'m'`) 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": [
        "boyer-moore",
        "caractere-absent"
      ],
      "title": "Caractère absent du motif",
      "partie": "boyer-moore",
      "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",
          "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.",
      "variants": [
        {
          "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",
              "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."
        },
        {
          "statement": "Avec le motif `\"code\"` (longueur 4), le caractère du\ntexte aligné avec la fin du motif est `'x'`. 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": "4",
              "correct": true,
              "feedback": "Bonne réponse : `'x'` n'apparaît pas dans `\"code\"`,\non peut donc sauter de toute la longueur du\nmotif (4) 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": "3",
              "correct": false,
              "feedback": "Erreur : 3 est le décalage pour `'c'`, 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."
        },
        {
          "statement": "Avec le motif `\"python\"` (longueur 6), le caractère du\ntexte aligné avec la fin du motif est `'q'`. 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": "6",
              "correct": true,
              "feedback": "Bonne réponse : `'q'` n'apparaît pas dans `\"python\"`,\non peut donc sauter de toute la longueur du\nmotif (6) 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": "5",
              "correct": false,
              "feedback": "Erreur : 5 est le décalage pour `'p'`, 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": [
        "boyer-moore",
        "table",
        "repetition"
      ],
      "title": "Caractère répété",
      "partie": "boyer-moore",
      "statement": "Pour le motif `\"maman\"` (longueur 5), quelle est la\ntable de décalages ?",
      "options": [
        {
          "text": "`{'m': 4, 'a': 3}`",
          "correct": false,
          "feedback": "Erreur : `'m'` apparaît plusieurs fois, on\nne garde que la position **la plus à droite**\n(hors dernière position), ici 2.\n"
        },
        {
          "text": "`{'m': 2, 'a': 3}`",
          "correct": false,
          "feedback": "Erreur : ce sont les positions des caractères, pas les\ndécalages ; le décalage vaut 5 - 1 - position.\n"
        },
        {
          "text": "`{'m': 2, 'a': 1}`",
          "correct": true,
          "feedback": "Bonne réponse : `'m'` en position 2, décalage 5 - 1 - 2 = 2 ; `'a'` en position 3, décalage 5 - 1 - 3 = 1.\n"
        },
        {
          "text": "`{'m': 2, 'a': 1, '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.",
      "variants": [
        {
          "statement": "Pour le motif `\"maman\"` (longueur 5), quelle est la\ntable de décalages ?",
          "options": [
            {
              "text": "`{'m': 4, 'a': 3}`",
              "correct": false,
              "feedback": "Erreur : `'m'` apparaît plusieurs fois, on\nne garde que la position **la plus à droite**\n(hors dernière position), ici 2.\n"
            },
            {
              "text": "`{'m': 2, 'a': 3}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions des caractères, pas les\ndécalages ; le décalage vaut 5 - 1 - position.\n"
            },
            {
              "text": "`{'m': 2, 'a': 1}`",
              "correct": true,
              "feedback": "Bonne réponse : `'m'` en position 2, décalage 5 - 1 - 2 = 2 ; `'a'` en position 3, décalage 5 - 1 - 3 = 1.\n"
            },
            {
              "text": "`{'m': 2, 'a': 1, '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."
        },
        {
          "statement": "Pour le motif `\"banane\"` (longueur 6), quelle est la\ntable de décalages ?",
          "options": [
            {
              "text": "`{'b': 5, 'a': 4, 'n': 3}`",
              "correct": false,
              "feedback": "Erreur : `'b'` apparaît plusieurs fois, on\nne garde que la position **la plus à droite**\n(hors dernière position), ici 0.\n"
            },
            {
              "text": "`{'b': 0, 'a': 3, 'n': 4}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions des caractères, pas les\ndécalages ; le décalage vaut 6 - 1 - position.\n"
            },
            {
              "text": "`{'b': 5, 'a': 2, 'n': 1}`",
              "correct": true,
              "feedback": "Bonne réponse : `'b'` en position 0, décalage 6 - 1 - 0 = 5 ; `'a'` en position 3, décalage 6 - 1 - 3 = 2 ; `'n'` en position 4, décalage 6 - 1 - 4 = 1.\n"
            },
            {
              "text": "`{'b': 5, 'a': 2, 'n': 1, 'e': 0}`",
              "correct": false,
              "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'e'`).\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."
        },
        {
          "statement": "Pour le motif `\"papa\"` (longueur 4), quelle est la\ntable de décalages ?",
          "options": [
            {
              "text": "`{'p': 3, 'a': 2}`",
              "correct": false,
              "feedback": "Erreur : `'p'` apparaît plusieurs fois, on\nne garde que la position **la plus à droite**\n(hors dernière position), ici 2.\n"
            },
            {
              "text": "`{'p': 2, 'a': 1}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions des caractères, pas les\ndécalages ; le décalage vaut 4 - 1 - position.\n"
            },
            {
              "text": "`{'p': 1, 'a': 2}`",
              "correct": true,
              "feedback": "Bonne réponse : `'p'` en position 2, décalage 4 - 1 - 2 = 1 ; `'a'` en position 1, décalage 4 - 1 - 1 = 2.\n"
            },
            {
              "text": "`{'p': 1, 'a': 0}`",
              "correct": false,
              "feedback": "Erreur : on **n'inclut pas** le dernier caractère\n(`'a'`).\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 ?",
      "partie": "boyer-moore",
      "statement": "Pourquoi exclut-on le dernier caractère du motif lors\ndu pré-traitement ?",
      "options": [
        {
          "text": "Parce qu'un décalage de 0 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 une quantité notable de 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",
      "partie": "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": [
        "pretraitement",
        "boyer-moore"
      ],
      "title": "Prétraiter le motif plutôt que le texte",
      "partie": "boyer-moore",
      "statement": "Avant de parcourir le texte, l'algorithme de Boyer-Moore calcule une table de décalages. Pourquoi ce prétraitement porte-t-il sur le **motif** et non sur le texte ?",
      "options": [
        {
          "text": "Le motif est court et connu d'avance : sa table sert à chaque échec",
          "correct": true,
          "feedback": "Bonne réponse : la table se calcule en un seul passage sur les m caractères du motif, puis elle est consultée à chaque échec, quelle que soit la longueur du texte parcouru.\n"
        },
        {
          "text": "Le texte ne peut pas être lu avant la recherche proprement dite",
          "correct": false,
          "feedback": "Erreur : rien n'empêche de lire le texte à l'avance. Mais le préparer demanderait de le parcourir en entier, ce que la recherche cherche justement à éviter.\n"
        },
        {
          "text": "La table du texte serait identique à celle du motif",
          "correct": false,
          "feedback": "Erreur : le texte contient en général d'autres caractères, à d'autres positions, que le motif. Une table construite sur le texte n'aurait rien à voir avec celle du motif.\n"
        },
        {
          "text": "Le motif est modifié pendant la recherche et doit donc être préparé",
          "correct": false,
          "feedback": "Erreur : le motif n'est jamais modifié pendant la recherche. La table est seulement lue pour savoir de combien décaler le motif.\n"
        }
      ],
      "explanation": "La table indique, pour chaque caractère, de combien décaler le motif lorsque ce caractère se trouve en face de la dernière position du motif. Elle ne dépend que du motif : on la calcule une seule fois, et on peut même la réutiliser pour chercher le même motif dans plusieurs textes."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "boyer-moore",
        "table",
        "code"
      ],
      "title": "Table produite par le code du prétraitement",
      "partie": "boyer-moore",
      "statement": "On considère la fonction suivante, qui construit la table de décalages de Boyer-Moore-Horspool :\n```python\ndef table_decalages(motif):\n    m = len(motif)\n    table = {}\n    for i in range(m - 1):\n        table[motif[i]] = m - 1 - i\n    return table\nprint(table_decalages(\"RIRE\"))\n```\nQu'affiche ce programme ?",
      "options": [
        {
          "text": "`{'R': 3, 'I': 2}`",
          "correct": false,
          "feedback": "Erreur : tu gardes la valeur de la première apparition de `R`. La boucle rencontre `R` une seconde fois, à l'indice 2, et l'affectation `table['R'] = 1` remplace l'ancienne valeur.\n"
        },
        {
          "text": "`{'R': 1, 'I': 2}`",
          "correct": true,
          "feedback": "Bonne réponse : avec m = 4, l’indice 0 associe `R` à 3, l’indice 1 associe `I` à 2, l’indice 2 associe `R` à 1 ; la dernière affectation pour `R` remplace la valeur 3.\n"
        },
        {
          "text": "`{'R': 1, 'I': 2, 'E': 0}`",
          "correct": false,
          "feedback": "Erreur : la boucle `for i in range(m - 1)` s'arrête à l'indice 2. Le dernier caractère, `E`, n'est jamais traité.\n"
        },
        {
          "text": "`{'R': 2, 'I': 1}`",
          "correct": false,
          "feedback": "Erreur : tu ranges l'indice `i` au lieu de `m - 1 - i`. La valeur stockée est la distance entre le caractère et la dernière position du motif.\n"
        }
      ],
      "explanation": "La table associe à chaque caractère du motif, sauf le dernier, sa distance à la fin du motif. Quand un caractère apparaît plusieurs fois, la dernière affectation l'emporte : on garde le plus petit décalage, le seul qui ne risque pas de faire manquer une occurrence.",
      "variants": [
        {
          "statement": "On considère la fonction suivante, qui construit la table de décalages de Boyer-Moore-Horspool :\n```python\ndef table_decalages(motif):\n    m = len(motif)\n    table = {}\n    for i in range(m - 1):\n        table[motif[i]] = m - 1 - i\n    return table\nprint(table_decalages(\"RIRE\"))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`{'R': 3, 'I': 2}`",
              "correct": false,
              "feedback": "Erreur : tu gardes la valeur de la première apparition de `R`. La boucle rencontre `R` une seconde fois, à l'indice 2, et l'affectation `table['R'] = 1` remplace l'ancienne valeur.\n"
            },
            {
              "text": "`{'R': 1, 'I': 2}`",
              "correct": true,
              "feedback": "Bonne réponse : avec m = 4, l’indice 0 associe `R` à 3, l’indice 1 associe `I` à 2, l’indice 2 associe `R` à 1 ; la dernière affectation pour `R` remplace la valeur 3.\n"
            },
            {
              "text": "`{'R': 1, 'I': 2, 'E': 0}`",
              "correct": false,
              "feedback": "Erreur : la boucle `for i in range(m - 1)` s'arrête à l'indice 2. Le dernier caractère, `E`, n'est jamais traité.\n"
            },
            {
              "text": "`{'R': 2, 'I': 1}`",
              "correct": false,
              "feedback": "Erreur : tu ranges l'indice `i` au lieu de `m - 1 - i`. La valeur stockée est la distance entre le caractère et la dernière position du motif.\n"
            }
          ],
          "explanation": "La table associe à chaque caractère du motif, sauf le dernier, sa distance à la fin du motif. Quand un caractère apparaît plusieurs fois, la dernière affectation l'emporte : on garde le plus petit décalage, le seul qui ne risque pas de faire manquer une occurrence."
        },
        {
          "statement": "On considère la fonction suivante, qui construit la table de décalages de Boyer-Moore-Horspool :\n```python\ndef table_decalages(motif):\n    m = len(motif)\n    table = {}\n    for i in range(m - 1):\n        table[motif[i]] = m - 1 - i\n    return table\nprint(table_decalages(\"PAPA\"))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`{'P': 3, 'A': 2}`",
              "correct": false,
              "feedback": "Erreur : tu gardes la valeur de la première apparition de `P`. La boucle rencontre `P` une seconde fois, à l'indice 2, et l'affectation `table['P'] = 1` remplace l'ancienne valeur.\n"
            },
            {
              "text": "`{'P': 1, 'A': 2}`",
              "correct": true,
              "feedback": "Bonne réponse : avec m = 4, l’indice 0 associe `P` à 3, l’indice 1 associe `A` à 2, l’indice 2 associe `P` à 1 ; la dernière affectation pour `P` remplace la valeur 3.\n"
            },
            {
              "text": "`{'P': 1, 'A': 0}`",
              "correct": false,
              "feedback": "Erreur : la boucle `for i in range(m - 1)` s'arrête à l'indice 2. Le dernier caractère, `A`, n'est jamais traité.\n"
            },
            {
              "text": "`{'P': 2, 'A': 1}`",
              "correct": false,
              "feedback": "Erreur : tu ranges l'indice `i` au lieu de `m - 1 - i`. La valeur stockée est la distance entre le caractère et la dernière position du motif.\n"
            }
          ],
          "explanation": "La table associe à chaque caractère du motif, sauf le dernier, sa distance à la fin du motif. Quand un caractère apparaît plusieurs fois, la dernière affectation l'emporte : on garde le plus petit décalage, le seul qui ne risque pas de faire manquer une occurrence."
        },
        {
          "statement": "On considère la fonction suivante, qui construit la table de décalages de Boyer-Moore-Horspool :\n```python\ndef table_decalages(motif):\n    m = len(motif)\n    table = {}\n    for i in range(m - 1):\n        table[motif[i]] = m - 1 - i\n    return table\nprint(table_decalages(\"NINON\"))\n```\nQu'affiche ce programme ?",
          "options": [
            {
              "text": "`{'N': 4, 'I': 3, 'O': 1}`",
              "correct": false,
              "feedback": "Erreur : tu gardes la valeur de la première apparition de `N`. La boucle rencontre `N` une seconde fois, à l'indice 2, et l'affectation `table['N'] = 2` remplace l'ancienne valeur.\n"
            },
            {
              "text": "`{'N': 2, 'I': 3, 'O': 1}`",
              "correct": true,
              "feedback": "Bonne réponse : avec m = 5, l’indice 0 associe `N` à 4, l’indice 1 associe `I` à 3, l’indice 2 associe `N` à 2, l’indice 3 associe `O` à 1 ; la dernière affectation pour `N` remplace la valeur 4.\n"
            },
            {
              "text": "`{'N': 0, 'I': 3, 'O': 1}`",
              "correct": false,
              "feedback": "Erreur : la boucle `for i in range(m - 1)` s'arrête à l'indice 3. Le dernier caractère, `N`, n'est jamais traité.\n"
            },
            {
              "text": "`{'N': 2, 'I': 1, 'O': 3}`",
              "correct": false,
              "feedback": "Erreur : tu ranges l'indice `i` au lieu de `m - 1 - i`. La valeur stockée est la distance entre le caractère et la dernière position du motif.\n"
            }
          ],
          "explanation": "La table associe à chaque caractère du motif, sauf le dernier, sa distance à la fin du motif. Quand un caractère apparaît plusieurs fois, la dernière affectation l'emporte : on garde le plus petit décalage, le seul qui ne risque pas de faire manquer une occurrence."
        }
      ]
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "pas-de-gain"
      ],
      "title": "Quand BM n'apporte rien",
      "partie": "analyse",
      "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 utilisé est particulièrement large",
          "correct": false,
          "feedback": "Erreur : c'est le cas **favorable** à\nBoyer-Moore (sauts maximaux fréquents).\n"
        },
        {
          "text": "Quand texte et motif sont presque uniformes (que des `'a'`)",
          "correct": true,
          "feedback": "Bonne réponse : quand texte et motif n'ont presque\nque des caractères identiques, l'alphabet utile est\nréduit, les décalages sont presque toujours de 1 et\non retombe sur un parcours position par position.\n"
        },
        {
          "text": "Quand le motif cherché est court (trois ou quatre caractères)",
          "correct": false,
          "feedback": "Partiellement vrai : les sauts sont limités à m,\ndonc le gain est modeste, mais il existe. Ce n'est\npas le **pire cas**.\n"
        },
        {
          "text": "Quand le texte parcouru est très long (plusieurs mégaoctets)",
          "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": [
        "cas-particuliers",
        "comparaison-caracteres"
      ],
      "title": "Majuscules et minuscules dans la recherche",
      "partie": "analyse",
      "statement": "La fonction `recherche_naif(texte, motif)` renvoie la liste des indices où le motif apparaît, en comparant les caractères un à un avec `==`. Que renvoie l'appel `recherche_naif(\"Le Chat dort\", \"chat\")` ?",
      "options": [
        {
          "text": "`[3]`",
          "correct": false,
          "feedback": "Erreur : `\"Chat\"` commence bien à l'indice 3, mais `C` et `c` sont deux caractères différents, de codes 67 et 99. La comparaison échoue dès le premier caractère du motif.\n"
        },
        {
          "text": "`[]`",
          "correct": true,
          "feedback": "Bonne réponse : la comparaison `==` distingue `C` (code 67) de `c` (code 99). Aucune position du texte ne correspond entièrement au motif `\"chat\"`.\n"
        },
        {
          "text": "`[4]`",
          "correct": false,
          "feedback": "Erreur : tu numérotes les caractères à partir de 1 et tu négliges la différence entre majuscule et minuscule. En réalité, aucune position ne convient.\n"
        },
        {
          "text": "Une erreur, car le texte contient des espaces",
          "correct": false,
          "feedback": "Erreur : l'espace est un caractère comme un autre. Il est simplement comparé aux caractères du motif, sans provoquer d'erreur.\n"
        }
      ],
      "explanation": "La recherche textuelle compare des caractères, et une majuscule n'est pas le même caractère que la minuscule correspondante. Pour trouver `\"Chat\"` quelle que soit son écriture, on peut convertir le texte et le motif avec `lower()` avant la recherche."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "boyer-moore",
        "alignements",
        "longueur-motif"
      ],
      "title": "Un motif plus long, moins d'alignements",
      "partie": "boyer-moore",
      "statement": "Un texte de 60 caractères ne contient que des points. On y cherche avec Boyer-Moore-Horspool d'abord le motif `\"abc\"`, puis le motif `\"abcdef\"`. Combien d'alignements l'algorithme examine-t-il dans chacun des deux cas ?",
      "options": [
        {
          "text": "58 puis 55",
          "correct": false,
          "feedback": "Erreur : 58 et 55 sont les nombres de positions testées par l'algorithme naïf, qui avance d'une seule position à chaque échec. Boyer-Moore saute bien plus loin.\n"
        },
        {
          "text": "20 puis 40",
          "correct": false,
          "feedback": "Erreur : tu penses qu'un motif plus long demande plus d'alignements. C'est l'inverse : le point étant absent du motif, chaque échec fait sauter le motif de toute sa longueur, donc de 6 pour `\"abcdef\"`.\n"
        },
        {
          "text": "20 puis 10",
          "correct": true,
          "feedback": "Bonne réponse : avec `\"abc\"`, les alignements sont 0, 3, 6, …, 57, soit 20 ; avec `\"abcdef\"`, ce sont 0, 6, 12, …, 54, soit 10.\n"
        },
        {
          "text": "60 puis 60",
          "correct": false,
          "feedback": "Erreur : l'algorithme ne s'aligne pas sur chaque caractère du texte. Chaque échec provoque un saut de toute la longueur du motif, puisque le point en est absent.\n"
        }
      ],
      "explanation": "Le point est absent des deux motifs : à chaque alignement, la première comparaison échoue et le motif saute de toute sa longueur. Plus le motif est long, plus les sauts sont grands, et moins l'algorithme examine d'alignements.",
      "variants": [
        {
          "statement": "Un texte de 60 caractères ne contient que des points. On y cherche avec Boyer-Moore-Horspool d'abord le motif `\"abc\"`, puis le motif `\"abcdef\"`. Combien d'alignements l'algorithme examine-t-il dans chacun des deux cas ?",
          "options": [
            {
              "text": "58 puis 55",
              "correct": false,
              "feedback": "Erreur : 58 et 55 sont les nombres de positions testées par l'algorithme naïf, qui avance d'une seule position à chaque échec. Boyer-Moore saute bien plus loin.\n"
            },
            {
              "text": "20 puis 40",
              "correct": false,
              "feedback": "Erreur : tu penses qu'un motif plus long demande plus d'alignements. C'est l'inverse : le point étant absent du motif, chaque échec fait sauter le motif de toute sa longueur, donc de 6 pour `\"abcdef\"`.\n"
            },
            {
              "text": "20 puis 10",
              "correct": true,
              "feedback": "Bonne réponse : avec `\"abc\"`, les alignements sont 0, 3, 6, …, 57, soit 20 ; avec `\"abcdef\"`, ce sont 0, 6, 12, …, 54, soit 10.\n"
            },
            {
              "text": "60 puis 60",
              "correct": false,
              "feedback": "Erreur : l'algorithme ne s'aligne pas sur chaque caractère du texte. Chaque échec provoque un saut de toute la longueur du motif, puisque le point en est absent.\n"
            }
          ],
          "explanation": "Le point est absent des deux motifs : à chaque alignement, la première comparaison échoue et le motif saute de toute sa longueur. Plus le motif est long, plus les sauts sont grands, et moins l'algorithme examine d'alignements."
        },
        {
          "statement": "Un texte de 90 caractères ne contient que des points. On y cherche avec Boyer-Moore-Horspool d'abord le motif `\"xyz\"`, puis le motif `\"xyzxyzxyz\"`. Combien d'alignements l'algorithme examine-t-il dans chacun des deux cas ?",
          "options": [
            {
              "text": "88 puis 82",
              "correct": false,
              "feedback": "Erreur : 88 et 82 sont les nombres de positions testées par l'algorithme naïf, qui avance d'une seule position à chaque échec. Boyer-Moore saute bien plus loin.\n"
            },
            {
              "text": "30 puis 90",
              "correct": false,
              "feedback": "Erreur : tu penses qu'un motif plus long demande plus d'alignements. C'est l'inverse : le point étant absent du motif, chaque échec fait sauter le motif de toute sa longueur, donc de 9 pour `\"xyzxyzxyz\"`.\n"
            },
            {
              "text": "30 puis 10",
              "correct": true,
              "feedback": "Bonne réponse : avec `\"xyz\"`, les alignements sont 0, 3, 6, …, 87, soit 30 ; avec `\"xyzxyzxyz\"`, ce sont 0, 9, 18, …, 81, soit 10.\n"
            },
            {
              "text": "90 puis 90",
              "correct": false,
              "feedback": "Erreur : l'algorithme ne s'aligne pas sur chaque caractère du texte. Chaque échec provoque un saut de toute la longueur du motif, puisque le point en est absent.\n"
            }
          ],
          "explanation": "Le point est absent des deux motifs : à chaque alignement, la première comparaison échoue et le motif saute de toute sa longueur. Plus le motif est long, plus les sauts sont grands, et moins l'algorithme examine d'alignements."
        },
        {
          "statement": "Un texte de 48 caractères ne contient que des points. On y cherche avec Boyer-Moore-Horspool d'abord le motif `\"code\"`, puis le motif `\"codecode\"`. Combien d'alignements l'algorithme examine-t-il dans chacun des deux cas ?",
          "options": [
            {
              "text": "45 puis 41",
              "correct": false,
              "feedback": "Erreur : 45 et 41 sont les nombres de positions testées par l'algorithme naïf, qui avance d'une seule position à chaque échec. Boyer-Moore saute bien plus loin.\n"
            },
            {
              "text": "12 puis 24",
              "correct": false,
              "feedback": "Erreur : tu penses qu'un motif plus long demande plus d'alignements. C'est l'inverse : le point étant absent du motif, chaque échec fait sauter le motif de toute sa longueur, donc de 8 pour `\"codecode\"`.\n"
            },
            {
              "text": "12 puis 6",
              "correct": true,
              "feedback": "Bonne réponse : avec `\"code\"`, les alignements sont 0, 4, 8, …, 44, soit 12 ; avec `\"codecode\"`, ce sont 0, 8, 16, …, 40, soit 6.\n"
            },
            {
              "text": "48 puis 48",
              "correct": false,
              "feedback": "Erreur : l'algorithme ne s'aligne pas sur chaque caractère du texte. Chaque échec provoque un saut de toute la longueur du motif, puisque le point en est absent.\n"
            }
          ],
          "explanation": "Le point est absent des deux motifs : à chaque alignement, la première comparaison échoue et le motif saute de toute sa longueur. Plus le motif est long, plus les sauts sont grands, et moins l'algorithme examine d'alignements."
        }
      ]
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "trace-complete"
      ],
      "title": "Nombre d'alignements de BM",
      "partie": "boyer-moore",
      "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": [
        "boyer-moore",
        "table",
        "motif-long"
      ],
      "title": "Table d'un motif complexe",
      "partie": "boyer-moore",
      "statement": "Quelle est la table de décalages de\nBoyer-Moore-Horspool pour le motif `\"abracadabra\"`\n(longueur 11) ?",
      "options": [
        {
          "text": "`{'a': 10, 'b': 9, 'r': 8, 'c': 6, 'd': 4}`",
          "correct": false,
          "feedback": "Erreur : ce sont les décalages calculés sur la\n**première** position de chaque caractère. Il faut\nprendre la dernière position (hors fin).\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 → 11-1-8=2 ; r en 9 → 11-1-9=1 ; c en 4 → 11-1-4=6 ; d en 6 → 11-1-6=4. Le `'a'` final est exclu.\n"
        },
        {
          "text": "`{'a': 7, 'b': 8, 'r': 9, 'c': 4, 'd': 6}`",
          "correct": false,
          "feedback": "Erreur : ce sont les positions, pas les\ndécalages. Le décalage est m - 1 - position.\n"
        },
        {
          "text": "`{'a': 0, 'b': 2, 'r': 1, 'c': 6, 'd': 4}`",
          "correct": false,
          "feedback": "Erreur : le `'a'` final est **exclu** ; sa dernière\nposition retenue est celle qui précède la fin du motif.\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.",
      "variants": [
        {
          "statement": "Quelle est la table de décalages de\nBoyer-Moore-Horspool pour le motif `\"abracadabra\"`\n(longueur 11) ?",
          "options": [
            {
              "text": "`{'a': 10, 'b': 9, 'r': 8, 'c': 6, 'd': 4}`",
              "correct": false,
              "feedback": "Erreur : ce sont les décalages calculés sur la\n**première** position de chaque caractère. Il faut\nprendre la dernière position (hors fin).\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 → 11-1-8=2 ; r en 9 → 11-1-9=1 ; c en 4 → 11-1-4=6 ; d en 6 → 11-1-6=4. Le `'a'` final est exclu.\n"
            },
            {
              "text": "`{'a': 7, 'b': 8, 'r': 9, 'c': 4, 'd': 6}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les\ndécalages. Le décalage est m - 1 - position.\n"
            },
            {
              "text": "`{'a': 0, 'b': 2, 'r': 1, 'c': 6, 'd': 4}`",
              "correct": false,
              "feedback": "Erreur : le `'a'` final est **exclu** ; sa dernière\nposition retenue est celle qui précède la fin du motif.\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."
        },
        {
          "statement": "Quelle est la table de décalages de\nBoyer-Moore-Horspool pour le motif `\"tictactoc\"`\n(longueur 9) ?",
          "options": [
            {
              "text": "`{'t': 8, 'i': 7, 'c': 6, 'a': 4, 'o': 1}`",
              "correct": false,
              "feedback": "Erreur : ce sont les décalages calculés sur la\n**première** position de chaque caractère. Il faut\nprendre la dernière position (hors fin).\n"
            },
            {
              "text": "`{'t': 2, 'i': 7, 'c': 3, 'a': 4, 'o': 1}`",
              "correct": true,
              "feedback": "Bonne réponse. Dernière position (hors fin) de\nchaque caractère : t en 6 → 9-1-6=2 ; i en 1 → 9-1-1=7 ; c en 5 → 9-1-5=3 ; a en 4 → 9-1-4=4 ; o en 7 → 9-1-7=1. Le `'c'` final est exclu.\n"
            },
            {
              "text": "`{'t': 6, 'i': 1, 'c': 5, 'a': 4, 'o': 7}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les\ndécalages. Le décalage est m - 1 - position.\n"
            },
            {
              "text": "`{'t': 2, 'i': 7, 'c': 0, 'a': 4, 'o': 1}`",
              "correct": false,
              "feedback": "Erreur : le `'c'` final est **exclu** ; sa dernière\nposition retenue est celle qui précède la fin du motif.\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."
        },
        {
          "statement": "Quelle est la table de décalages de\nBoyer-Moore-Horspool pour le motif `\"barbapapa\"`\n(longueur 9) ?",
          "options": [
            {
              "text": "`{'b': 8, 'a': 7, 'r': 6, 'p': 3}`",
              "correct": false,
              "feedback": "Erreur : ce sont les décalages calculés sur la\n**première** position de chaque caractère. Il faut\nprendre la dernière position (hors fin).\n"
            },
            {
              "text": "`{'b': 5, 'a': 2, 'r': 6, 'p': 1}`",
              "correct": true,
              "feedback": "Bonne réponse. Dernière position (hors fin) de\nchaque caractère : b en 3 → 9-1-3=5 ; a en 6 → 9-1-6=2 ; r en 2 → 9-1-2=6 ; p en 7 → 9-1-7=1. Le `'a'` final est exclu.\n"
            },
            {
              "text": "`{'b': 3, 'a': 6, 'r': 2, 'p': 7}`",
              "correct": false,
              "feedback": "Erreur : ce sont les positions, pas les\ndécalages. Le décalage est m - 1 - position.\n"
            },
            {
              "text": "`{'b': 5, 'a': 0, 'r': 6, 'p': 1}`",
              "correct": false,
              "feedback": "Erreur : le `'a'` final est **exclu** ; sa dernière\nposition retenue est celle qui précède la fin du motif.\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": [
        "table-decalages",
        "alignements"
      ],
      "title": "Le texte qui ralentit Boyer-Moore",
      "partie": "boyer-moore",
      "statement": "On cherche le motif `\"ABCD\"` avec Boyer-Moore-Horspool dans un texte formé de 20 fois la même lettre. Pour lequel des textes suivants l'algorithme examine-t-il **le plus** d'alignements ?",
      "options": [
        {
          "text": "20 fois la lettre `B`",
          "correct": false,
          "feedback": "Erreur : la table associe `B` à un décalage de 2. Les alignements 0, 2, …, 16 font 9 alignements, moins qu'avec la lettre `C`, dont le décalage n'est que de 1.\n"
        },
        {
          "text": "20 fois la lettre `C`",
          "correct": true,
          "feedback": "Bonne réponse : la table associe `C` à un décalage de 1, le plus petit possible. L'algorithme examine alors toutes les positions de 0 à 16, soit 17 alignements, autant que l'algorithme naïf.\n"
        },
        {
          "text": "20 fois la lettre `A`",
          "correct": false,
          "feedback": "Erreur : la table associe `A` à un décalage de 3. On n'examine que les alignements 0, 3, …, 15, soit 6.\n"
        },
        {
          "text": "20 fois la lettre `X`",
          "correct": false,
          "feedback": "Erreur : `X` est absent du motif, le décalage vaut donc 4, la longueur du motif. On n'examine que les alignements 0, 4, 8, 12 et 16, soit 5.\n"
        }
      ],
      "explanation": "Pour `\"ABCD\"`, la table associe `A` à 3, `B` à 2, `C` à 1, et tout autre caractère à 4. À chaque alignement, c'est la lettre du texte située en face de la dernière position du motif qui fixe le saut : plus ce décalage est petit, plus l'algorithme examine d'alignements."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "debug"
      ],
      "title": "Bug subtil",
      "partie": "naif",
      "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",
      "partie": "analyse",
      "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 construit sa table à partir du motif seul",
          "correct": false,
          "feedback": "Vrai : le prétraitement ne lit que le motif, avant tout parcours du texte. La même table sert donc pour n'importe quel texte.\n"
        },
        {
          "text": "Boyer-Moore-Horspool compare le motif au texte de droite à gauche",
          "correct": false,
          "feedback": "Vrai : à chaque alignement, la comparaison commence par le dernier caractère du motif et remonte vers le premier.\n"
        },
        {
          "text": "Boyer-Moore-Horspool est toujours plus rapide que l'algorithme naïf",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : sur certains textes (alphabet réduit, motif répétitif), les décalages restent très petits et Boyer-Moore examine autant d'alignements que le naïf, avec en plus le travail de consultation de la table.\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",
      "partie": "naif",
      "statement": "Quelle fonction Python illustre l'algorithme naïf de\nrecherche textuelle par comparaison **caractère par\ncaractère**, en renvoyant la liste des indices de\ndébut de chaque occurrence ?",
      "options": [
        {
          "text": "```python\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": "```python\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": "```python\ndef recherche_naif(texte, motif):\n    positions = []\n    for i in range(len(texte)):\n        if texte[i] == motif[0]:\n            positions.append(i)\n    return positions\n```\n",
          "correct": false,
          "feedback": "Erreur : on ne compare que le premier\ncaractère du motif, jamais les suivants.\nPour `motif = \"abra\"` dans\n`\"abracadabra\"`, ce code renvoie\n`[0, 3, 5, 7, 10]`, toutes les positions\nde `'a'`, alors que les occurrences de\n`\"abra\"` commencent aux indices `0` et `7`.\n"
        },
        {
          "text": "```python\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",
      "partie": "boyer-moore",
      "statement": "Avec le motif `motif = \"exercice\"` (longueur 8) et\nle texte `\"é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."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "analyse",
        "complexite",
        "taille-du-texte"
      ],
      "title": "Un texte deux fois plus long",
      "partie": "analyse",
      "statement": "On cherche le même motif dans un texte deux fois plus long, avec\nl'algorithme naïf. Comment évolue le temps de recherche, dans le\npire cas ?",
      "options": [
        {
          "text": "Il double environ, le coût étant proportionnel à la longueur du texte",
          "correct": true,
          "feedback": "Bonne réponse : le pire cas coûte de l'ordre de $n \\times m$\ncomparaisons ; à motif fixé, le coût est linéaire en $n$.\n"
        },
        {
          "text": "Il quadruple, le coût étant proportionnel au carré de la longueur",
          "correct": false,
          "feedback": "Erreur : le facteur $m$ est la longueur du motif, qui n'a pas\nchangé.\n"
        },
        {
          "text": "Il ne change pas, seule la longueur du motif comptant",
          "correct": false,
          "feedback": "Erreur : chaque position du texte est un alignement à tester.\n"
        },
        {
          "text": "Il est divisé par deux, les occurrences étant plus faciles à trouver",
          "correct": false,
          "feedback": "Erreur : un texte plus long ne rend rien plus facile.\n"
        }
      ],
      "explanation": "Le coût du naïf est en $O(n \\times m)$ : linéaire en chacune des\ndeux longueurs."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "analyse",
        "boyer-moore",
        "meilleur-cas",
        "calcul"
      ],
      "title": "Le meilleur cas de Boyer-Moore",
      "partie": "analyse",
      "statement": "Un texte de $1000$ caractères, un motif de $10$ caractères. Dans\nle meilleur cas, à chaque alignement, le dernier caractère comparé\nest absent du motif et l'algorithme de Boyer-Moore saute de $10$\npositions. Combien de comparaisons fait-il environ ?",
      "options": [
        {
          "text": "$100$, une par saut de $10$",
          "correct": true,
          "feedback": "Bonne réponse : une seule comparaison par alignement, et\n$1000 / 10 = 100$ alignements. Le texte est lu en sautant la\nplupart de ses caractères.\n"
        },
        {
          "text": "$1000$, une par caractère du texte",
          "correct": false,
          "feedback": "Erreur : les caractères sautés ne sont jamais comparés.\n"
        },
        {
          "text": "$10000$, le pire cas du naïf",
          "correct": false,
          "feedback": "Erreur : $n \\times m$ est le pire cas de l'algorithme naïf, à\nl'opposé de ce meilleur cas.\n"
        },
        {
          "text": "$10$, une par caractère du motif",
          "correct": false,
          "feedback": "Erreur : il faut bien parcourir tout le texte, par sauts.\n"
        }
      ],
      "explanation": "Boyer-Moore peut faire moins de comparaisons que le texte n'a de\ncaractères : un motif long sur un alphabet riche donne des sauts\nlongs.",
      "variants": [
        {
          "statement": "Un texte de $1000$ caractères, un motif de $10$ caractères. Dans\nle meilleur cas, à chaque alignement, le dernier caractère comparé\nest absent du motif et l'algorithme de Boyer-Moore saute de $10$\npositions. Combien de comparaisons fait-il environ ?",
          "options": [
            {
              "text": "$100$, une par saut de $10$",
              "correct": true,
              "feedback": "Bonne réponse : une seule comparaison par alignement, et\n$1000 / 10 = 100$ alignements. Le texte est lu en sautant la\nplupart de ses caractères.\n"
            },
            {
              "text": "$1000$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : les caractères sautés ne sont jamais comparés.\n"
            },
            {
              "text": "$10000$, le pire cas du naïf",
              "correct": false,
              "feedback": "Erreur : $n \\times m$ est le pire cas de l'algorithme naïf, à\nl'opposé de ce meilleur cas.\n"
            },
            {
              "text": "$10$, une par caractère du motif",
              "correct": false,
              "feedback": "Erreur : il faut bien parcourir tout le texte, par sauts.\n"
            }
          ],
          "explanation": "Boyer-Moore peut faire moins de comparaisons que le texte n'a de\ncaractères : un motif long sur un alphabet riche donne des sauts\nlongs."
        },
        {
          "statement": "Un texte de $600$ caractères, un motif de $5$ caractères. Dans\nle meilleur cas, à chaque alignement, le dernier caractère comparé\nest absent du motif et l'algorithme de Boyer-Moore saute de $5$\npositions. Combien de comparaisons fait-il environ ?",
          "options": [
            {
              "text": "$120$, une par saut de $5$",
              "correct": true,
              "feedback": "Bonne réponse : une seule comparaison par alignement, et\n$600 / 5 = 120$ alignements. Le texte est lu en sautant la\nplupart de ses caractères.\n"
            },
            {
              "text": "$600$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : les caractères sautés ne sont jamais comparés.\n"
            },
            {
              "text": "$3000$, le pire cas du naïf",
              "correct": false,
              "feedback": "Erreur : $n \\times m$ est le pire cas de l'algorithme naïf, à\nl'opposé de ce meilleur cas.\n"
            },
            {
              "text": "$5$, une par caractère du motif",
              "correct": false,
              "feedback": "Erreur : il faut bien parcourir tout le texte, par sauts.\n"
            }
          ],
          "explanation": "Boyer-Moore peut faire moins de comparaisons que le texte n'a de\ncaractères : un motif long sur un alphabet riche donne des sauts\nlongs."
        },
        {
          "statement": "Un texte de $2000$ caractères, un motif de $8$ caractères. Dans\nle meilleur cas, à chaque alignement, le dernier caractère comparé\nest absent du motif et l'algorithme de Boyer-Moore saute de $8$\npositions. Combien de comparaisons fait-il environ ?",
          "options": [
            {
              "text": "$250$, une par saut de $8$",
              "correct": true,
              "feedback": "Bonne réponse : une seule comparaison par alignement, et\n$2000 / 8 = 250$ alignements. Le texte est lu en sautant la\nplupart de ses caractères.\n"
            },
            {
              "text": "$2000$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : les caractères sautés ne sont jamais comparés.\n"
            },
            {
              "text": "$16000$, le pire cas du naïf",
              "correct": false,
              "feedback": "Erreur : $n \\times m$ est le pire cas de l'algorithme naïf, à\nl'opposé de ce meilleur cas.\n"
            },
            {
              "text": "$8$, une par caractère du motif",
              "correct": false,
              "feedback": "Erreur : il faut bien parcourir tout le texte, par sauts.\n"
            }
          ],
          "explanation": "Boyer-Moore peut faire moins de comparaisons que le texte n'a de\ncaractères : un motif long sur un alphabet riche donne des sauts\nlongs."
        }
      ]
    },
    {
      "id": "q30",
      "difficulty": 1,
      "skills": [
        "analyse",
        "occurrences-multiples",
        "algorithme"
      ],
      "title": "Trouver toutes les occurrences",
      "partie": "analyse",
      "statement": "L'algorithme de recherche renvoie la position de la première\noccurrence. Que faut-il changer pour obtenir toutes les\noccurrences ?",
      "options": [
        {
          "text": "Mémoriser chaque position trouvée et continuer le parcours au lieu de renvoyer",
          "correct": true,
          "feedback": "Bonne réponse : au lieu d'un `return` dès la première\noccurrence, on ajoute la position à une liste et on poursuit\njusqu'à la fin du texte.\n"
        },
        {
          "text": "Relancer la recherche depuis le début du texte après chaque occurrence",
          "correct": false,
          "feedback": "Erreur : on retrouverait toujours la même première occurrence.\n"
        },
        {
          "text": "Rien, un algorithme de recherche ne peut trouver qu'une occurrence",
          "correct": false,
          "feedback": "Erreur : il suffit de ne pas s'arrêter à la première.\n"
        },
        {
          "text": "Chercher le motif à l'envers pour trouver la dernière occurrence",
          "correct": false,
          "feedback": "Erreur : on obtiendrait une occurrence, pas toutes.\n"
        }
      ],
      "explanation": "Une recherche de toutes les occurrences renvoie une liste de\npositions, éventuellement vide."
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "analyse",
        "cas-limite",
        "longueur"
      ],
      "title": "Un motif plus long que le texte",
      "partie": "analyse",
      "statement": "Le motif cherché est plus long que le texte. Que renvoie une\nrecherche naïve correctement écrite, et au prix de combien de\ncomparaisons ?",
      "options": [
        {
          "text": "Aucune occurrence, sans aucune comparaison de caractères",
          "correct": true,
          "feedback": "Bonne réponse : il n'existe aucun alignement possible, la boucle\nsur les positions ne fait aucun tour.\n"
        },
        {
          "text": "Une erreur d'exécution, l'indice dépassant la fin du texte",
          "correct": false,
          "feedback": "Erreur : une boucle bornée par `n - m + 1` ne fait aucun tour\nquand cette borne est négative.\n"
        },
        {
          "text": "Une occurrence à la position $0$, le début coïncidant",
          "correct": false,
          "feedback": "Erreur : une occurrence exige que tout le motif soit présent.\n"
        },
        {
          "text": "Aucune occurrence, après avoir comparé tous les caractères du texte",
          "correct": false,
          "feedback": "Erreur : aucune comparaison n'est nécessaire, faute\nd'alignement.\n"
        }
      ],
      "explanation": "Les cas limites, motif vide ou motif plus long que le texte, se\ntestent avant tout : un bon algorithme les traite sans travail."
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "analyse",
        "naif",
        "pire-cas",
        "calcul"
      ],
      "title": "Le pire cas du naïf en chiffres",
      "partie": "analyse",
      "statement": "Le texte est formé de $10$ lettres `a`. Le motif est formé de\n$2$ lettres `a` suivies d'un `b`. L'algorithme naïf compare de\ngauche à droite et s'arrête au premier échec. Combien de\ncomparaisons fait-il en tout ?",
      "options": [
        {
          "text": "$24$, soit $8$ alignements de $3$ comparaisons",
          "correct": true,
          "feedback": "Bonne réponse : à chaque alignement, les $2$ premiers\ncaractères coïncident et seul le `b` final échoue : $3$\ncomparaisons, pour chacun des $8$ alignements.\n"
        },
        {
          "text": "$10$, une par caractère du texte",
          "correct": false,
          "feedback": "Erreur : chaque alignement relit les mêmes caractères.\n"
        },
        {
          "text": "$8$, une seule par alignement tenté",
          "correct": false,
          "feedback": "Erreur : chaque alignement coûte $3$ comparaisons, l'échec\nn'arrivant qu'au dernier caractère.\n"
        },
        {
          "text": "$30$, soit $10$ alignements de $3$ comparaisons",
          "correct": false,
          "feedback": "Erreur : il n'y a que $8$ alignements possibles.\n"
        }
      ],
      "explanation": "Un texte répétitif et un motif qui n'échoue qu'à la fin font\npayer au naïf son coût maximal, proche de $n \\times m$.",
      "variants": [
        {
          "statement": "Le texte est formé de $10$ lettres `a`. Le motif est formé de\n$2$ lettres `a` suivies d'un `b`. L'algorithme naïf compare de\ngauche à droite et s'arrête au premier échec. Combien de\ncomparaisons fait-il en tout ?",
          "options": [
            {
              "text": "$24$, soit $8$ alignements de $3$ comparaisons",
              "correct": true,
              "feedback": "Bonne réponse : à chaque alignement, les $2$ premiers\ncaractères coïncident et seul le `b` final échoue : $3$\ncomparaisons, pour chacun des $8$ alignements.\n"
            },
            {
              "text": "$10$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : chaque alignement relit les mêmes caractères.\n"
            },
            {
              "text": "$8$, une seule par alignement tenté",
              "correct": false,
              "feedback": "Erreur : chaque alignement coûte $3$ comparaisons, l'échec\nn'arrivant qu'au dernier caractère.\n"
            },
            {
              "text": "$30$, soit $10$ alignements de $3$ comparaisons",
              "correct": false,
              "feedback": "Erreur : il n'y a que $8$ alignements possibles.\n"
            }
          ],
          "explanation": "Un texte répétitif et un motif qui n'échoue qu'à la fin font\npayer au naïf son coût maximal, proche de $n \\times m$."
        },
        {
          "statement": "Le texte est formé de $8$ lettres `a`. Le motif est formé de\n$1$ lettres `a` suivies d'un `b`. L'algorithme naïf compare de\ngauche à droite et s'arrête au premier échec. Combien de\ncomparaisons fait-il en tout ?",
          "options": [
            {
              "text": "$14$, soit $7$ alignements de $2$ comparaisons",
              "correct": true,
              "feedback": "Bonne réponse : à chaque alignement, les $1$ premiers\ncaractères coïncident et seul le `b` final échoue : $2$\ncomparaisons, pour chacun des $7$ alignements.\n"
            },
            {
              "text": "$8$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : chaque alignement relit les mêmes caractères.\n"
            },
            {
              "text": "$7$, une seule par alignement tenté",
              "correct": false,
              "feedback": "Erreur : chaque alignement coûte $2$ comparaisons, l'échec\nn'arrivant qu'au dernier caractère.\n"
            },
            {
              "text": "$16$, soit $8$ alignements de $2$ comparaisons",
              "correct": false,
              "feedback": "Erreur : il n'y a que $7$ alignements possibles.\n"
            }
          ],
          "explanation": "Un texte répétitif et un motif qui n'échoue qu'à la fin font\npayer au naïf son coût maximal, proche de $n \\times m$."
        },
        {
          "statement": "Le texte est formé de $12$ lettres `a`. Le motif est formé de\n$3$ lettres `a` suivies d'un `b`. L'algorithme naïf compare de\ngauche à droite et s'arrête au premier échec. Combien de\ncomparaisons fait-il en tout ?",
          "options": [
            {
              "text": "$36$, soit $9$ alignements de $4$ comparaisons",
              "correct": true,
              "feedback": "Bonne réponse : à chaque alignement, les $3$ premiers\ncaractères coïncident et seul le `b` final échoue : $4$\ncomparaisons, pour chacun des $9$ alignements.\n"
            },
            {
              "text": "$12$, une par caractère du texte",
              "correct": false,
              "feedback": "Erreur : chaque alignement relit les mêmes caractères.\n"
            },
            {
              "text": "$9$, une seule par alignement tenté",
              "correct": false,
              "feedback": "Erreur : chaque alignement coûte $4$ comparaisons, l'échec\nn'arrivant qu'au dernier caractère.\n"
            },
            {
              "text": "$48$, soit $12$ alignements de $4$ comparaisons",
              "correct": false,
              "feedback": "Erreur : il n'y a que $9$ alignements possibles.\n"
            }
          ],
          "explanation": "Un texte répétitif et un motif qui n'échoue qu'à la fin font\npayer au naïf son coût maximal, proche de $n \\times m$."
        }
      ]
    },
    {
      "id": "q33",
      "difficulty": 2,
      "skills": [
        "naif",
        "alignement",
        "comparaisons",
        "trace"
      ],
      "title": "Les comparaisons d'un seul alignement",
      "partie": "naif",
      "statement": "À un alignement donné, l'algorithme naïf compare le motif `abxyz`\nà la portion de texte `abcde`, caractère par caractère de gauche à\ndroite, et s'arrête au premier échec. Combien de comparaisons\nfait-il ?",
      "options": [
        {
          "text": "$3$, jusqu'au premier échec inclus",
          "correct": true,
          "feedback": "Bonne réponse : les premiers caractères coïncident, et la\ncomparaison qui échoue est la $3$-ième ; l'alignement est\nabandonné aussitôt.\n"
        },
        {
          "text": "$5$, tous les caractères du motif",
          "correct": false,
          "feedback": "Erreur : après un échec, inutile de comparer la suite.\n"
        },
        {
          "text": "$1$, le premier caractère seulement",
          "correct": false,
          "feedback": "Erreur : la comparaison continue tant que les caractères\ncoïncident.\n"
        },
        {
          "text": "$0$, les deux mots étant différents",
          "correct": false,
          "feedback": "Erreur : savoir qu'ils diffèrent demande justement des\ncomparaisons.\n"
        }
      ],
      "explanation": "L'arrêt au premier échec est ce qui rend le naïf rapide en\npratique : la plupart des alignements échouent dès les premiers\ncaractères.",
      "variants": [
        {
          "statement": "À un alignement donné, l'algorithme naïf compare le motif `abxyz`\nà la portion de texte `abcde`, caractère par caractère de gauche à\ndroite, et s'arrête au premier échec. Combien de comparaisons\nfait-il ?",
          "options": [
            {
              "text": "$3$, jusqu'au premier échec inclus",
              "correct": true,
              "feedback": "Bonne réponse : les premiers caractères coïncident, et la\ncomparaison qui échoue est la $3$-ième ; l'alignement est\nabandonné aussitôt.\n"
            },
            {
              "text": "$5$, tous les caractères du motif",
              "correct": false,
              "feedback": "Erreur : après un échec, inutile de comparer la suite.\n"
            },
            {
              "text": "$1$, le premier caractère seulement",
              "correct": false,
              "feedback": "Erreur : la comparaison continue tant que les caractères\ncoïncident.\n"
            },
            {
              "text": "$0$, les deux mots étant différents",
              "correct": false,
              "feedback": "Erreur : savoir qu'ils diffèrent demande justement des\ncomparaisons.\n"
            }
          ],
          "explanation": "L'arrêt au premier échec est ce qui rend le naïf rapide en\npratique : la plupart des alignements échouent dès les premiers\ncaractères."
        },
        {
          "statement": "À un alignement donné, l'algorithme naïf compare le motif `help!`\nà la portion de texte `hello`, caractère par caractère de gauche à\ndroite, et s'arrête au premier échec. Combien de comparaisons\nfait-il ?",
          "options": [
            {
              "text": "$4$, jusqu'au premier échec inclus",
              "correct": true,
              "feedback": "Bonne réponse : les premiers caractères coïncident, et la\ncomparaison qui échoue est la $4$-ième ; l'alignement est\nabandonné aussitôt.\n"
            },
            {
              "text": "$5$, tous les caractères du motif",
              "correct": false,
              "feedback": "Erreur : après un échec, inutile de comparer la suite.\n"
            },
            {
              "text": "$1$, le premier caractère seulement",
              "correct": false,
              "feedback": "Erreur : la comparaison continue tant que les caractères\ncoïncident.\n"
            },
            {
              "text": "$0$, les deux mots étant différents",
              "correct": false,
              "feedback": "Erreur : savoir qu'ils diffèrent demande justement des\ncomparaisons.\n"
            }
          ],
          "explanation": "L'arrêt au premier échec est ce qui rend le naïf rapide en\npratique : la plupart des alignements échouent dès les premiers\ncaractères."
        },
        {
          "statement": "À un alignement donné, l'algorithme naïf compare le motif `pyle`\nà la portion de texte `pile`, caractère par caractère de gauche à\ndroite, et s'arrête au premier échec. Combien de comparaisons\nfait-il ?",
          "options": [
            {
              "text": "$2$, jusqu'au premier échec inclus",
              "correct": true,
              "feedback": "Bonne réponse : les premiers caractères coïncident, et la\ncomparaison qui échoue est la $2$-ième ; l'alignement est\nabandonné aussitôt.\n"
            },
            {
              "text": "$4$, tous les caractères du motif",
              "correct": false,
              "feedback": "Erreur : après un échec, inutile de comparer la suite.\n"
            },
            {
              "text": "$1$, le premier caractère seulement",
              "correct": false,
              "feedback": "Erreur : la comparaison continue tant que les caractères\ncoïncident.\n"
            },
            {
              "text": "$0$, les deux mots étant différents",
              "correct": false,
              "feedback": "Erreur : savoir qu'ils diffèrent demande justement des\ncomparaisons.\n"
            }
          ],
          "explanation": "L'arrêt au premier échec est ce qui rend le naïf rapide en\npratique : la plupart des alignements échouent dès les premiers\ncaractères."
        }
      ]
    },
    {
      "id": "q34",
      "difficulty": 1,
      "skills": [
        "naif",
        "decalage",
        "principe"
      ],
      "title": "Le décalage après un échec",
      "partie": "naif",
      "statement": "Dans l'algorithme naïf, de combien de positions décale-t-on le\nmotif après un alignement qui échoue ?",
      "options": [
        {
          "text": "D'une seule, quel que soit l'endroit de l'échec",
          "correct": true,
          "feedback": "Bonne réponse : le naïf ne tire aucune information de l'échec ;\nil essaie la position suivante.\n"
        },
        {
          "text": "De la longueur du motif, pour ne pas revenir en arrière",
          "correct": false,
          "feedback": "Erreur : on manquerait les occurrences qui commencent à\nl'intérieur de l'alignement raté.\n"
        },
        {
          "text": "Jusqu'au caractère qui a provoqué l'échec",
          "correct": false,
          "feedback": "Erreur : c'est une idée de Boyer-Moore, pas du naïf.\n"
        },
        {
          "text": "De deux positions, pour aller plus vite",
          "correct": false,
          "feedback": "Erreur : sauter une position manquerait des occurrences.\n"
        }
      ],
      "explanation": "Le naïf avance toujours d'un cran ; Boyer-Moore gagne en avançant\ndavantage quand c'est sans risque."
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "boyer-moore",
        "cas-limite",
        "motif-court"
      ],
      "title": "Boyer-Moore avec un motif d'un seul caractère",
      "partie": "boyer-moore",
      "statement": "On cherche un motif réduit à un seul caractère avec l'algorithme\nde Boyer-Moore. Comment se comporte-t-il ?",
      "options": [
        {
          "text": "Il examine chaque caractère du texte, comme le naïf",
          "correct": true,
          "feedback": "Bonne réponse : le saut maximal vaut la longueur du motif, soit\n$1$ ; chaque alignement coûte une comparaison et avance d'une\nposition. Aucun gain possible.\n"
        },
        {
          "text": "Il saute de deux caractères en deux, grâce à sa table",
          "correct": false,
          "feedback": "Erreur : un saut de deux manquerait un caractère du texte.\n"
        },
        {
          "text": "Il échoue aussitôt, la table de décalage étant vide",
          "correct": false,
          "feedback": "Erreur : une table vide donne le saut par défaut, $1$, et tout\nfonctionne.\n"
        },
        {
          "text": "Il ne compare qu'un caractère du texte sur deux",
          "correct": false,
          "feedback": "Erreur : chaque caractère du texte doit être examiné.\n"
        }
      ],
      "explanation": "L'avantage de Boyer-Moore grandit avec la longueur du motif ; sur\nun motif très court, il se réduit au naïf."
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "analyse",
        "mot-entier",
        "delimiteurs"
      ],
      "title": "Chercher un mot entier",
      "partie": "analyse",
      "statement": "La recherche du motif `art` dans `partir` renvoie une occurrence,\nalors que l'on voulait le mot entier. Comment restreindre la\nrecherche aux mots entiers ?",
      "options": [
        {
          "text": "Vérifier que les caractères voisins de l'occurrence ne sont pas des lettres",
          "correct": true,
          "feedback": "Bonne réponse : une occurrence est un mot entier si elle est\nbordée, de chaque côté, par un espace, une ponctuation ou le\nbord du texte.\n"
        },
        {
          "text": "Chercher le motif en majuscules, les mots entiers commençant par une majuscule",
          "correct": false,
          "feedback": "Erreur : la casse n'a aucun rapport avec les limites de mots.\n"
        },
        {
          "text": "Utiliser Boyer-Moore, qui ne trouve que des mots entiers",
          "correct": false,
          "feedback": "Erreur : Boyer-Moore cherche des sous-chaînes, exactement comme\nle naïf.\n"
        },
        {
          "text": "Ajouter un espace au début du motif seulement",
          "correct": false,
          "feedback": "Erreur : cela manquerait les mots en début de texte et\naccepterait `artiste`.\n"
        }
      ],
      "explanation": "Les algorithmes de recherche travaillent sur des caractères, pas\nsur des mots : la notion de mot entier s'ajoute par un test sur\nles voisins."
    }
  ]
}