Reputation: 11697
Let's say I have a list of tuples like so:
list_of_tuples = [('A', 'R'), ('B', 'R'), ('C', 'G'), ('D', 'G'), ('E', 'B'), ('D', 'B'), ('R', 'B'), ('F', 'R'), ('V', 'R'), ('A', 'G')]
The second value in each tuple will always either be R
, B
, or G
. I'd like to create a function validate
that checks to see if a certain word can be constructed using the letters in the first position of each tuple, but only if the letters in the section position of that tuple are not repeated.
For example, it's possible to construct the word:
ACE
with (A, R)
, (C, G)
and (E, B)
since the second value in each tuple corresponds to RGB
which doesn't repeat any letter consecutively.
ACED
with (A, R), (C, G), (E, B), and ('D', 'B')
is not possible since that would correspond to RGBB
in which there is a consecutive B.
Note that sometimes the same letter can have different letter in its second position, for example:
('A', 'R') and ('A', 'G')
. You'd only be able to spell ACE
if you selected the first tuple, not the second, otherwise the G
's would repeat.
Also note that combinations like GBRBG
are possible even though the second position letters "repeat" they don't repeat consecutively.
So I'd like a function that can validate words in the following way:
def validate(submitted_word, list_of_tuples)
One possibility is to construct every possible combination of sequences that are possible with this set and the corresponding sequences that would be produced with the letters in the second sequence, filter out the ones that are valid words, and then filter out the ones that have consecutive repeats of letters, but I worry that will be to inefficient given how big the list of tuples can become.
Upvotes: 1
Views: 301
Reputation: 23945
We can use backtracking, where the state is the count of each of R, G, B used per letter, as well as the previous "RGB" chosen, as we construct the word.
Python code (not memoized):
def f(i, word, prev, state):
if i == len(word):
return True
letter = word[i]
for second in ["R", "G", "B"]:
if state[letter][second] and prev != second:
state[letter][second] -= 1
is_valid = f(i + 1, word, second, state)
state[letter][second] += 1
if is_valid:
return True
return False
def get_counts(tuples):
result = {}
for letter, rgb in tuples:
if letter in result:
result[letter][rgb] += 1
else:
result[letter] = {"R": 0, "G": 0, "B": 0}
result[letter][rgb] = 1
return result
tuples = [('A', 'R'), ('B', 'R'), ('C', 'G'), ('D', 'G'), ('E', 'B'), ('D', 'B'), ('R', 'B'), ('F', 'R'), ('V', 'R'), ('A', 'G')]
counts = get_counts(tuples)
print f(0, "ACE", "", counts) # True
print f(0, "ACED", "", counts) # True
print f(0, "BF", "", counts) # False
Upvotes: 1
Reputation: 10624
I built the following. Firstly we create a function that compares 2 lists and checks if they have at least one different element. Secondly we run this function for the assosiated lists of all consecutive pairs of letters in the word. I considered that all letters of the word are represented in the list_of_tuples. Otherwise it will need a couple of lines more to check for existance (let me know in that case, so that i will add them).
def validate(word, list_of_tuples):
def rgb(l1,l2):
return len(set(l1).difference(set(l2)))>0
l=[[i, [k[1] for k in list_of_tuples if k[0]==i]] for i in word]
for i in range(len(l)-1):
if not rgb(l[i][1], l[i+1][1]):
return False
return True
Upvotes: 0
Reputation: 1742
See below for a self-contained solution and tests:
list_of_tuples = [
('A', 'R'),
('B', 'R'),
('C', 'G'),
('D', 'G'),
('E', 'B'),
('D', 'B'),
('R', 'B'),
('F', 'R'),
('V', 'R'),
('A', 'G')
]
def validate(submitted_word, list_of_tuples):
# Check length of word
if len(submitted_word) == 0:
raise ValueError("len(submitted_word) must be > 0")
# Initialise options for first character
options = [[tup for tup in list_of_tuples if tup[0] == submitted_word[0]]]
# Iterate through the rest of the characters
for char in submitted_word[1:]:
# Initialise set of characters in second position of previous tuple
forbidden_chars = set(tup[1] for tup in options[-1])
# Add valid options for the next character
options.append([
tup
for tup in list_of_tuples
if (tup[0] == char) and len(forbidden_chars - set(tup[1])) > 0
])
# If there are no options, then submitted_word does not validate
if len(options[-1]) == 0:
print(options)
return False
print(options)
return True
print(validate("ACE", list_of_tuples))
print()
print(validate("ACED", list_of_tuples))
print()
print(validate("ACFFFED", list_of_tuples))
Console output:
[[('A', 'R'), ('A', 'G')], [('C', 'G')], [('E', 'B')]]
True
[[('A', 'R'), ('A', 'G')], [('C', 'G')], [('E', 'B')], [('D', 'G')]]
True
[[('A', 'R'), ('A', 'G')], [('C', 'G')], [('F', 'R')], []]
False
Upvotes: 1
Reputation: 77850
This is a graph traversal problem. Your available nodes are the tuples of (letter, color); your edges are the letter transitions in the given word.
For each input, "simply" construct the graph for that word. Given ACE, you have
Layer 1 -- transition START to any A
START -> AR
START -> AG
Layer 2 -- transition A to C
AR -> CG
not allowed: AG -> CG
Layer 3 -- transition C to E
CG -> EB
Layer 4 -- transition any E to GOAL
EB -> GOAL
Now you simply apply the graph traversal function (any self-respecting graph package has one) to solve your spelling problem.
Upvotes: 1