简体   繁体   中英

Modifying a set within a recursive function?

I'm trying to write a program to check if a particular word can be made using a given "boggle board". There full details of the challenge are here: Boggle Word Checker .

Basically, the program is supposed find the first letter of a word, on the board, and then check if any of the adjacent letters to it on the board match the next letter of the word.

This is what I've got so far (not pretty I know):

def pos(board, x, y):
    # function to get value from a grid, but return None if not on grid,to
    # prevent wraparound
    if x >= 0 and y >= 0 and x < len(board[0]) and y < len(board):
        return board[y][x]
    else:
        return None

def surrounds(board, x, y):
    # make a dictionary which to store the positions and values of adjacent
    # letters on board, given a single postision as input
    return {
        (x-1,y-1) : pos(board,x-1,y-1), #aboveLeft
        (x,y-1) : pos(board,x,y-1),     #aboveMiddle etc...
        (x+1,y-1) : pos(board,x+1,y-1),
        (x-1,y) : pos(board,x-1,y),
        (x+1,y) : pos(board,x+1,y),
        (x-1,y+1) : pos(board,x-1,y+1),
        (x,y+1) : pos(board,x,y+1),
        (x+1,y+1) : pos(board,x+1,y+1)
    }

def find_word(board, word):
    # initialise
    listOfCoords = []

    # find all occurrences of the first letter, and store their board
    # position in a list
    for i in range(len(board)):
        for j in range(len(board[0])):
            if board[i][j] == word[0]:
                listOfCoords.append([j,i])

    print('list of ' + word[0] + 's on board:')
    print(listOfCoords)
    print()

    # if word is only 1 letter long then we can return True at this point
    if listOfCoords and len(word) == 1:
        return True

    # otherwise we move on to look for the second letter
    return findnext(board,word,listOfCoords)

def findnext(board, word, mylist):
    for x, y in mylist:
        print("Current coords: {},{}\n".format(x,y))
        surroundings = surrounds(board,x,y)

        listFounds = []

        for k, v in surroundings.items():

            if v == word[1]:
                print("{} found at {}".format(v,k))
                print()

                if len(word) == 2:
                    print()
                    return True

                listFounds.append(k)

                if findnext(board, word[1:], listFounds) == True:
                    return True
    return False

testBoard = [
      ["E","A","R","A"],
      ["N","L","E","C"],
      ["I","A","I","S"],
      ["B","Y","O","R"]
    ]

print(find_word(testBoard, "CEREAL"))

However, I've encountered a problem, as the challenge specifies that no position on the board can be used more than once. Therefore, in the above example, the program should return False for "CEREAL", but mine returns True.

I was thinking a way around this could be to use a set, which adds the coordinates to the set once a next letter is found. However I'm a bit lost as to where I would need to create the empty set, and how it would work with all the loops and recursion going on...

For example, let's say we were looking for "CEREAL" on a different board, which has 2 Es adjacent to C. let's say the first path only leads to CER and the other leads to CEREAL. If we go down the CER path first, the positions for it would be added to the set, and I would somehow need to remove them again before it goes down the CEREAL path.

I'm struggling to think how to implement this in my program.

You need to have an array of booleans the size of the board all set to False. When you use a letter and call your function recursively, set the cell to True for the used letter. When returning from the recursive call, set the cell value back to False. You should only use letters with False values indicating that they have not been previously used.

Alternatively, you can replace the used letter by None after keeping the letter in a temporary variable then do the recursive call. Upon the return from the recursive call put back the value of the cell from the temp variable.

Steps:

  1. Find the 1st character match against the board
  2. Once a match is found, do a DFS with backtracking till you completely match the word or exhaust the search due to mismatch.
  3. If complete match is not found in above step, continue scanning the board. (ie go to step 1) for the next character that matches on the board.
  4. If a match is found in step 3, report success.

This solution works for NxM board as well.

def is_within_bounds(row, col, row_dim, col_dim):
    return row >= 0 and row < row_dim and col >= 0 and col < col_dim


def get_neighbors(row, col, row_dim, col_dim):
    for r_offset in (-1, 0, 1):
        for c_offset in (-1, 0, 1):
            if r_offset == 0 and c_offset == 0:
                continue
            r_new = row + r_offset
            c_new = col + c_offset
            if is_within_bounds(r_new, c_new, row_dim, col_dim):
                yield (r_new, c_new)


def is_word_found(board, word, row, col, visited):
    if word[0] != board[row][col]:
        return False
    if len(word) == 1:
        return True
    for neighbor in get_neighbors(row, col, len(board), len(board[0])):
        if neighbor not in visited:
            visited.add(neighbor)
            if is_word_found(board, word[1:], neighbor[0], neighbor[1], visited):
                return True
            visited.remove(neighbor)
    return False


def find_word(board, word):
    for row in range(len(board)):
        for col in range(len(board[0])):
            if word[0] != board[row][col]:
                continue
            if is_word_found(board, word, row, col, set()):
                return True
    return False

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM