7
votes

C ++ - Comment savoir efficacement si une chaîne d'un vecteur peut être assemblée à partir d'un ensemble de lettres

Je suis en train de mettre en œuvre une version textuelle de Scrabble pour un projet collégial.

J'ai un vecteur contenant environ 400k chaînes (mon dictionnaire) et, à un moment donné à chaque tour, je vais devoir vérifier s'il reste un mot dans le dictionnaire qui peut être formé avec le Pièces dans la main du joueur. Je vérifie si le joueur a un geste à gauche ... sinon, c'est le jeu sur le joueur en question ...

Ma seule solution à ceci est itération à travers la chaîne, une à une et à l'aide d'une sous-routine, je dois vérifier si la chaîne en question peut être formée à partir des pièces du joueur. Je vais mettre en œuvre une vérification QuickFail si l'utilisateur a des voyelles, mais cela sera toujours inefficace malheureusement.

Le fichier texte contenant le dictionnaire est déjà commandé par ordre alphabétique, de sorte que le vecteur est trié.

Toute suggestion?

Un problème a été présenté dans les commentaires ci-dessous: Toute suggestion sur comment puis-je prendre les lettres déjà sur la Commission?


3 commentaires

La question n'est donc pas vraiment "comment savoir efficacement sur un vecteur", mais plutôt "comment savoir efficacement si un mot de la collection peut être assemblé à partir d'un ensemble de lettres"?


Vous ne semblez pas prendre en compte dans votre problème de description que des mots peuvent être formés en fonction du conseil ainsi que de la main du joueur.


Oh. Je ne prenais pas cela en compte. Grande, encore plus de complexité ajoutée à un problème complexe déjà (pour mon niveau de connaissances)


6 Réponses :


8
votes

Sans vous donner un code spécifique (puisqu'il s'agit de devoirs après tout), une approche générale à prendre en compte consiste à carte des lettres triées dans le mot aux mots légaux réels.

que est de dire que si votre fichier de dictionnaire n'avait que les mots singe , gum et tasse , votre structure de données ressemblerait à: xxx

alors vous pouvez simplement passer à des permutations des lettres du joueur et identifier rapidement si cette clé existe sur la carte.

Vous payez un peu de temps de traitement Configuration du dictionnaire au démarrage, mais vous ne devez ensuite effectuer quelques recherches rapides plutôt que d'itération de toute la liste à chaque fois.


5 commentaires

C'est exactement ce que j'étais en train de taper.


C'est ainsi que Jon Bentley décrit son algorithme de détection / de création Anagram dans "Perles de programmation". C'est aussi faux: cela identifierait uniquement les mots qui peuvent être produits avec tous les lettres du joueur.


@JemFinch: Vous avez raison de dire que cela ne permet pas à une seule recherche de déterminer toutes les anagrammes de tous les sous-ensembles des lettres du joueur, mais j'ai spécifié dans ma réponse que vous auriez besoin d'effectuer plusieurs recherches.


"Ensuite, vous pouvez simplement passer des permutations des lettres du joueur" - je suis désolé, je suis débutant, comment puis-je générer ces permutations?


@Mark j'ai manqué que vous feriez plusieurs recherches. Le problème est alors que vous auriez toujours besoin de faire 127 recherches; pas exactement efficace.



1
votes

Vous pouvez également stocker les chaînes avec des caractères triés dans l'ordre asciibétique dans un STD :: Set, puis trier les lettres du lecteur dans le même ordre et recherchez la carte pour chaque sous-chaîne des lettres du joueur.


0 commentaires

1
votes

Que diriez-vous de garder les paires {mot du dictionnaire, une chaîne constituée des mêmes lettres mais en ordre croissant (tri)}

Trier le vecteur de ces paires en fonction de la deuxième chaîne et comparez à l'aide de la recherche binaire avec une chaîne composée de lettres triées de la main des joueurs.


0 commentaires

2
votes

sonne comme une variante du sous-ensemble Sum Problème: http://fr.wikipedia.org/wiki / Subset_sum_problem

Peut-être que certains des algorithmes décrits vous aideraient.


0 commentaires

2
votes

Il y a eu de nombreux papiers et des questions sur Scrabble sur ce site.

Il existe de nombreuses stratégies disponibles. P>

La représentation de votre dictionnaire est inadéquate, il existe de nombreuses méthodes intelligentes disponibles. Par exemple, vérifiez ce qu'une trie est sur wikipedia. P>

Utilisation de celui-ci, vous pouvez implémenter un algorithme de retour en arrière pour déterminer rapidement quels mots que vous pouvez former. P>

 1. I have a 'a' (but 'a' is not a word)
 2. I have a 'p' (but 'ap' is not a word)
 3. I don't have any 'e' so I can't go further, let's backtrack
 4. I don't have any 's' so...
 5. I have a 'g', but it's not a word
 6. I have a 'u', but 'gu' is not a word
 7. I have a 'm' and 'gum' is a word, I store it somewhere, I can't go further


2 commentaires

Merci pour votre réponse approfondie. Au début de mon projet, j'ai pensé à une trie, mais je voulais éviter de mettre en œuvre une telle structure de données compliquée. J'ai trouvé une bonne implémentation d'un arbre Radix en ligne et j'ai obtenu un «clair» de mon instructeur de l'utiliser. Pensez-vous que cela le couperait?


L'arbre Radix est une trie "spatio-efficacité", le principe est le même afin que cela fonctionne certainement. Cependant, le problème principal avec votre logique: il suffit d'essayer de former des mots avec les lettres de votre possession.) Essayez de rechercher un Scrabble sur donc si vous voulez plus d'indices.



1
votes

Il y a déjà de bonnes réponses ici, et je pense qu'une trie est probablement la bonne façon d'y aller, mais c'est un problème intéressant, je vais donc lancer dans mes deux cents 'Worthing ...

L'approche naïve serait de générer toutes les permutations des lettres disponibles et de tous les sous-ensembles distincts, puis recherchez chaque mot potentiel dans le dictionnaire. Le problème est que, bien qu'il ne soit pas difficile de le faire, il existe un nombre étonnamment important de mots potentiels, et la plupart d'entre eux sont invalides.

du côté positif, la vérification du dictionnaire peut être accélérée avec une recherche binaire ou quelque chose de similaire. Du côté négatif, vous le feriez tant de fois que le programme serrait à une halte pour de longues listes de lettres.

Nous avons définitivement besoin de prétraitement du dictionnaire pour le rendre plus utile et ce dont nous avons vraiment besoin, c'est d'avoir un moyen d'exclure rapidement la plupart des matchs potentiels, même si la méthode présente des faux positifs occasionnels.

Un moyen de faire cela serait de représenter quelles lettres utilisées par un mot dans une carte de bit. En d'autres termes, précalculer un nombre 32 bits pour chaque mot dans le dictionnaire, où chaque bit est défini si la lettre correspondante de l'alphabet est utilisée dans le mot au moins une fois. Cela vous permettrait de trouver tous les mots potentiels en effectuant une analyse linéaire du dictionnaire et de ne conserver que celles qui utilisent uniquement des lettres que vous avez disponibles. Je soupçonne que, avec un peu d'intelligence et d'indexation, vous pouvez faire mieux que linéaire.

des candidats que vous trouvez, certains auront besoin de plus d'instances d'une lettre que de votre disposition. Celles-ci seront donc de faux positifs. Cela signifie que vous devez faire un chèque final sur tous les candidats que vous avez générés pour éliminer les quasi-hits. Il existe de nombreuses façons de le faire, mais l'un des plus simples consiste à parcourir votre liste de lettres et à remplacer la première occurrence de cette lettre dans le mot potentiel avec un tiret. Lorsque vous avez terminé, si le mot potentiel a quelque chose que des tirets, c'est un échec. Une solution plus élégante, mais pas nécessairement plus rapide, serait de générer une gamme de fréquences de lettre et de les comparer.

Encore une fois, je pense que les essais sont probablement la voie à suivre, mais j'espère que ces idées vous sont utiles.

modifier

Permettez-moi de jeter un exemple de la manière dont vous pourriez faire mieux qu'une recherche linéaire complète de la recherche initiale: utilisez le radix. Gardez un index simple qui vous permet de rechercher le premier mot qui commence par une lettre donnée. Ensuite, lorsque vous faites la recherche, sautez tous les mots qui commencent par une lettre que vous n'avez pas. Ce n'est pas un gigantesque vitesse, mais c'est une amélioration.


1 commentaires

Je ne vais pas éditer plus avant, mais je me sens obligé de mentionner que les filtres de Bloom seraient un excellent moyen de vérifier toute liste de mots potentiels contre le dictionnaire, en ce qu'ils sont rapides et ne permettent pas de faux négatifs.