Python 以字母网格中的坐标列表的形式查找单词的位置

Python 以字母网格中的坐标列表的形式查找单词的位置,python,algorithm,Python,Algorithm,给定一个字母网格和一个单词列表,以坐标列表的形式查找每个单词的位置。结果列表可以是任意顺序,但必须按顺序给出单个单词的坐标。字母不能在单词和字母之间重复使用。每个给定的单词都保证在网格中。单词的连续字母要么向下,要么向右(即没有倒转的单词或单词的倒转部分,只有向下或向右) 例如,给定以下网格和一组单词 [ ['d', 'r', 'd', 'o', 'r', 's'], ['o', 'b', 'i', 'g', 'n', 'c'], ['g', 'f', 'n', 'm'

给定一个字母网格和一个单词列表,以坐标列表的形式查找每个单词的位置。结果列表可以是任意顺序,但必须按顺序给出单个单词的坐标。字母不能在单词和字母之间重复使用。每个给定的单词都保证在网格中。单词的连续字母要么向下,要么向右(即没有倒转的单词或单词的倒转部分,只有向下或向右)

例如,给定以下网格和一组单词

 [
    ['d', 'r', 'd', 'o', 'r', 's'],
    ['o', 'b', 'i', 'g', 'n', 'c'],
    ['g', 'f', 'n', 'm', 't', 'a'],
    ['x', 's', 'i', 'a', 'n', 't']
]

words1 = [ "dog", "dogma", "cat" ]
输出以下坐标列表:

findWords(grid, words)->
  [ [ (1, 5), (2, 5), (3, 5) ], # cat
    [ (0, 2), (0, 3), (1, 3), (2, 3), (3, 3)], # dogma
    [ (0, 0), (1, 0), (2, 0) ], # dog
  ]

在本例中,“dogma”中的“dog”不能用于单词“dog”,因为字母不能重复使用。

以下是我尝试的解决方案。首先,我找到所有可能的途径来拼写任何单词。路径由其拼写的单词索引。然后,我通过每次为每个单词添加一条可能的路径,同时维护一个SEED集合,来迭代所有可能的路径组合。一旦在我找到所有可行的路径之前,我跑出了一个词的可行路径,然后我就回过头来

def findWords(grid, words):
    # Regular old dfs through the grid, we only go right or down
    def dfs(row, col, path, idx):
        if idx == len(word):
            if word in all_paths:
                all_paths[word].append(list(path))
            else:
                all_paths[word] = [list(path)]
        else:
            if row + 1 < len(grid):
                if grid[row+1][col] == word[idx]:
                    path.append((row+1, col))
                    dfs(row+1, col, path, idx+1)
                    path.pop()
            if col + 1 < len(grid[0]):
                if grid[row][col+1] == word[idx]:
                    path.append((row, col+1))
                    dfs(row, col+1, path, idx+1)
                    path.pop()

    # For each word, find all possible paths through the grid to spell the word
    # Each path is a collection of coordinates as is desired from the function
    # Paths are indexed by word and stored in a list in a dictionary
    all_paths = {}
    for row in range(len(grid)):
        for col in range(len(grid[0])):
            for word in words:
                if grid[row][col] == word[0]:
                    dfs(row, col, [(row, col)], 1)

    # Try all possible combinations of paths from each letter
    def dfs2(idx):
        if idx == len(words):
            return True

        word = words[idx]
        for path in all_paths[word]:
            for loc in path:
                if loc in seen:
                    return False
            for loc in path:
                seen.add(loc)
            if dfs2(idx+1):
                retlst.append(path)
                return True
            else:
                for loc in path:
                    seen.remove(loc)
        return False

    # Backtrack through possible combinations
    seen = set([])
    retlst = []
    dfs2(0)
    return retlst
def findWords(网格,单词):
#常规的旧dfs通过网格,我们只能向右或向下移动
def dfs(行、列、路径、idx):
如果idx==len(字):
如果所有_路径中都有单词:
所有路径[word]。追加(列表(路径))
其他:
所有路径[word]=[列表(路径)]
其他:
如果行+1
可能有一种方法可以通过可能的路径组合进行DFS,而您正在通过需要拼写的单词进行DFS,以避免预先计算所有路径,但这对我来说太复杂了。

根据答案,首先您要制作一个字典,将字母映射到位置:

board=[
[d',r',d',o',r',s'],
[o',b',i',g',n',c'],
[g',f',n',m',t',a'],
['x','s','i','a','n','t']
]
单词=[“狗”、“教条”、“猫”]
字母_位置={}
对于y,枚举中的行(板):
对于x,枚举(行)中的字母:
字母_positions.setdefault(字母,[])。追加((x,y))
>>字母位置
{'d':[(0,0),(2,0)],
'r':[(1,0),(4,0)],
‘o’:[(3,0)、(0,1)],
's':[(5,0),(1,3)],
"b":[(1,1)],,
‘i’:[(2,1)、(2,3)],
‘g’:[(3,1),(0,2)],
‘n’:[(4,1)、(2,2)、(4,3)],
"c":[(5,1)],,
‘f’:[(1,2)],
‘m’:[(3,2)],
‘t’:[(4,2),(5,3)],
‘a’:[(5,2)、(3,3)],
'x':[(0,3)]}
与链接答案一样,您应该跟踪有效的移动。另外,您只能向下或向右移动,因此,与原始答案相比,我添加了一个加号条件。我没有更改
find_word
函数

def是否有效移动(位置,最后):
如果last=[]:
返回真值
如果位置[0]abs(位置[0]-最后[0])在网格中查找单词的任务可以通过其他答案中提供的解决方案来完成,也可以通过尝试、后缀树或数组来完成

例如,根据@péter Leéh给出的答案,这将是一个使用
python3
查找所有路径的修改版本:

grid=[
[d',r',d',o',r',s'],
[o',b',i',g',n',c'],
[g',f',n',m',t',a'],
['x','s','i','a','n','t']
]
单词1=[“狗”、“教条”、“猫”]
#构建密集网格
密集网格={}
对于行,枚举(网格)中的行:
对于列,枚举中的字母(第行):
密集网格.setdefault(字母,[]).append((行,列))
#查找所有单词的所有路径
def是否有效移动(p,q):
返回(p[0]==q[0]和p[1]+1==q[1])或(p[0]+1==q[0]和p[1]==q[1])
def查找所有路径(当前位置、后缀、密集网格=密集网格):
如果len(后缀)==0:
返回[[当前位置]]
可能的_后缀_路径=[]
对于密集网格中的pos[后缀[0]]:
如果移动有效(当前位置,位置):
可能的_后缀_路径+=查找所有_路径(位置,后缀[1:])
#由于职位列表是有序的,我可以跳过其余的
elif位置[0]-当前位置[0]>=2:
打破
返回[[curr\u pos]+p表示可能的后缀路径中的p]
单词_路径=[
[密集网格中pos的路径[word[0]]用于查找所有路径中的路径(pos,word[1:])]
对于单词中的单词1
]
最后一个密集网格是一个从字符到列表的字典
{
    'd': [(0, 0), (0, 2)],
    'r': [(0, 1), (0, 4)],
    'o': [(0, 3), (1, 0)],
    's': [(0, 5), (3, 1)],
    'b': [(1, 1)],
    'i': [(1, 2), (3, 2)],
    'g': [(1, 3), (2, 0)],
    'n': [(1, 4), (2, 2), (3, 4)],
    'c': [(1, 5)],
    'f': [(2, 1)],
    'm': [(2, 3)],
    't': [(2, 4), (3, 5)],
    'a': [(2, 5), (3, 3)],
    'x': [(3, 0)]
}
[
    [
         [(0, 0), (1, 0), (2, 0)], # dog
         [(0, 2), (0, 3), (1, 3)]
    ],
    [
         [(0, 2), (0, 3), (1, 3), (2, 3), (3, 3)] # dogma
    ],
    [
         [(1, 5), (2, 5), (3, 5)] # cat
    ]
]
[
    [(0, 0), (1, 0), (2, 0)], # dog
    [(0, 2), (0, 3), (1, 3), (2, 3), (3, 3)], # dogma
    [(1, 5), (2, 5), (3, 5)] # cat
]
from bisect import bisect_left

def find_words(board, words, x, y, prefix, path):
    ' Find words that can be generated starting at position x, y '
    
    # Base case
    # find if current word prefix is in list of words
    found = bisect_left(words, prefix)  # can use binary search since words are sorted
    if found >= len(words):
        return
   
    if words[found] == prefix:
        yield prefix, path              # Prefix in list of words

    # Give up on path if what we found is not even a prefix
    # (there is no point in going further)
    if len(words[found]) < len(prefix) or words[found][:len(prefix)] != prefix:
        return
    
    # Extend path by one lettter in boarde
    # Since can only go right and down 
    # No need to worry about same cell occurring multiple times in a given path
    for adj_x, adj_y in [(0, 1), (1, 0)]:
        x_new, y_new = x + adj_x, y + adj_y
        if x_new < len(board) and y_new < len(board[0]):
            yield from find_words(board, words, x_new, y_new, \
                                  prefix + board[x_new][y_new], \
                                  path + [(x_new, y_new)])
     
def check_all_starts(board, words):
    ' find all possilble paths through board for generating words '
    # check each starting point in board
    for x in range(len(board)):
        for y in range(len(board[0])):
            yield from find_words(board, words, x, y, board[x][y], [(x, y)])
   
def find_non_overlapping(choices, path):
    ' Find set of choices with non-overlapping paths '
    if not choices:
        # Base case
        yield path
    else:
        word, options = choices[0]

        for option in options:
            set_option = set(option)
            
            if any(set_option.intersection(p) for w, p in path):
                # overlaps with path
                continue
            else:
                yield from find_non_overlapping(choices[1:], path + [(word, option)])
        
    
def solve(board, words):
    ' Solve for path through board to create words '
    words.sort()
    
    # Get choice of paths for each word
    choices = {}
    for word, path in check_all_starts(board, words):
        choices.setdefault(word, []).append(path)
    
    # Find non-intersecting paths (i.e. no two words should have a x, y in common)
    if len(choices) == len(words):
        return next(find_non_overlapping(list(choices.items()), []), None)
    
from pprint import pprint as pp

words = [ "dog", "dogma", "cat" ]
board = [
            ['d', 'r', 'd', 'o', 'r', 's'],
            ['o', 'b', 'i', 'g', 'n', 'c'],
            ['g', 'f', 'n', 'm', 't', 'a'],
            ['x', 's', 'i', 'a', 'n', 't']]

pp(solve(board, words))
        
Test 1
[('dog', [(0, 0), (1, 0), (2, 0)]),
 ('dogma', [(0, 2), (0, 3), (1, 3), (2, 3), (3, 3)]),
 ('cat', [(1, 5), (2, 5), (3, 5)])]
words = ["by","bat"] 
board = [ ['b', 'a', 't'], 
          ['y', 'x', 'b'], 
          ['x', 'x', 'y'], ] 

pp(solve(board, words))
Test 2
[('bat', [(0, 0), (0, 1), (0, 2)]), 
 ('by', [(1, 2), (2, 2)])]