Niveau 2
Scooby-Naire, le chien de Joseph Marchand, est très intelligent. Il arrive à communiquer avec son maître en tapant ses messages sur le clavier. Malheureusement, ses grosses pattes ne lui permettent pas d'être précis et il appuie sur les touches alentour.
De plus, Joseph Marchand, un peu zinzin, s'est trompé et a donné des croquettes pour chat à Scooby-Naire ! Du coup, celui-ci n'est plus trop sûr de l'ordre des lettres dans ses phrases et écrit un peu n'importe comment...
En revanche, le vocabulaire de Scooby-Naire étant assez limité, Joseph Marchand peut facilement tester les messages les plus courants. Pour l'y aider, vous devez écrire un programme qui renvoie 1 si la chaîne passée en paramètre peut-être contenue dans le message, 0 sinon.
1
si tous les caractères de la chaîne à tester sont contenus dans le message, 0
sinon. Attention : si le caractère A
est contenu 3 fois dans la chaîne à tester, il doit être contenu au moins autant de fois dans le message de Scooby-Naire.
Exemple d'entrée
56 IL FAUT TUER LA VIEILLE TATA, ELLE M'A TUE AVEC SON YOYO 11 TTY MA TUER
Exemple de sortie
1
Exemple d'entrée
91 Ca ne peut pas matcher, a une lettre pres en plus... De toute facon c'est totalement faux ! 9 K est nul
Exemple de sortie
0
""" auteur : Franck CHAMBON Régional 2011 - Problème 6 - Décryptage II https://prologin.org/train/2011/semifinal/decryptage2 """ # 0. Cœur du problème def est_inclus(extrait: str, texte: str) -> bool: """Renvoie True si tous les caractères de 'extrait' sont inclus dans 'texte', avec multiplicité, mais sans compter l'ordre. >>> est_inclus("ABAC", "BRAVO, CHARLIE.") 1 >>> est_inclus("ABBAC", "BRAVO, CHARLIE.") 0 """ CONST_ASCII = 128 def compte(morceau: str) -> list[int]: """Renvoie la liste du compte des caractères. >>> compte("ABAC")[ord('A')] 2 >>> compte("ABAC")[ord('D')] 0 """ compte_morceau = [0 for _ in range(CONST_ASCII)] for caractère in morceau: i = ord(caractère) compte_morceau[i] += 1 return compte_morceau # version fonctionnelle return all(cpt_extrait <= cpt_texte for cpt_extrait, cpt_texte in zip(compte(extrait), compte(texte))) # version itérative compte_extrait = compte(extrait) compte_texte = compte(texte) for i in range(CONST_ASCII): if compte_extrait[i] > compte_texte[i]: return False return True # 1. Lecture taille_message_scooby_naire = int(input()) message_scooby_naire = input() taille_message_test = int(input()) message_test = input() # 2. Écriture print(1 if est_inclus(message_test, message_scooby_naire) else 0)
Il n'était pas précisé dans l'énoncé que les caractères étaient tous en ASCII, donc avec un code (que l'on obtient avec ord
) compris entre et inclus.
Proposons une version avec un dictionnaire, qui est capable de fonctionner avec tous jeux de caractères.
Nous proposerons ensuite une version avec un type nouveau Counter
qui a été pensé justement pour les dictionnaires qui servent à compter des éléments d'un ensemble.
dict
# 0. Cœur du problème def est_inclus(extrait: str, texte: str) -> bool: """Renvoie True si tous les caractères de 'extrait' sont inclus dans 'texte', avec multiplicité, mais sans compter l'ordre. >>> est_inclus("ABAC", "BRAVO, CHARLIE.") 1 >>> est_inclus("ABBAC", "BRAVO, CHARLIE.") 0 """ def compte(morceau: str) -> dict: """Renvoie un `Counter` des caractères. >>> compte("ABAC") == {'A': 2, 'B': 1, 'C': 1} True >>> compte("ABBAC") == {'A': 2, 'B': 2, 'C': 1} True """ compte_morceau = dict() for caractère in morceau: if caractère not in compte_morceau: compte_morceau[caractère] = 1 else: compte_morceau[caractère] += 1 return compte_morceau # version itérative compte_extrait = compte(extrait) compte_texte = compte(texte) for caractère in compte_extrait: if caractère not in compte_texte: return False else: if compte_extrait[caractère] > compte_texte[caractère]: return False return True
On constate qu'il faut tester l'appartenance d'une clé au dictionnaire...
Counter
from collections import Counter # 0. Cœur du problème def est_inclus(extrait: str, texte: str) -> bool: """Renvoie True si tous les caractères de 'extrait' sont inclus dans 'texte', avec multiplicité, mais sans compter l'ordre. >>> est_inclus("ABAC", "BRAVO, CHARLIE.") 1 >>> est_inclus("ABBAC", "BRAVO, CHARLIE.") 0 """ def compte(morceau: str) -> Counter: """Renvoie un `Counter` des caractères. >>> compte("ABAC") == {'A': 2, 'B': 1, 'C': 1} True >>> compte("ABBAC") == {'A': 2, 'B': 2, 'C': 1} True """ compte_morceau = Counter() for caractère in morceau: compte_morceau[caractère] += 1 return compte_morceau # version itérative compte_extrait = compte(extrait) compte_texte = compte(texte) for caractère in compte_extrait: if compte_extrait[caractère] > compte_texte[caractère]: return False return True
Ici inutile de tester l'appartenance ; la valeur est renvoyée par défaut.
Counter
#... # version fonctionnelle compte_extrait = compte(extrait) compte_texte = compte(texte) return all(compte_extrait[caractère] <= compte_texte[caractère] for caractère in compte_extrait)
On constate qu'on peut facilement utiliser compte_texte[caractère]
qui est par défaut à si caractère
n'est pas une clé de compte_texte
. Chose que nous n'aurions pas pu faire avec un dictionnaire conventionnel.