我在软件开发人员面试中一直使用这个问题的一个变体,所以我对这个问题进行了相当多的思考。这是一个更好的答案:它可以处理任意数量的玩家、任意方形井字游戏和任意“运行规模”。该方法相当简单,提供有关找到的所有序列的信息,并且是 O(N),其中 N 是细胞数。
# Given a square tic-tac-toe grid of any size, with any number of players, find
# all sequences (horizontal, vertical, diagonal) of some minimum size.
def main():
raw_grid = [
[1, 1, 2, 1, 0], # Zero means open spot.
[0, 2, 1, 1, 1],
[2, 2, 2, 1, 2],
[1, 0, 1, 1, 2],
[1, 0, 0, 0, 2],
]
for run in get_runs(raw_grid, 3):
print run
def get_runs(raw_grid, run_size):
# Offsets to find the previous cell in all four directions.
offsets = {
'h' : ( 0, -1), # _
'v' : (-1, 0), # |
'f' : (-1, 1), # /
'b' : (-1, -1), # \
}
# Helpers to check for valid array bounds and to return a new cell dict.
size = len(raw_grid)
in_bounds = lambda r, c: r >= 0 and c >= 0 and r < size and c < size
new_cell = lambda i, j, p: dict(h=1, v=1, f=1, b=1, i=i, j=j, player=p)
# Use the raw grid to create a grid of cell dicts.
grid = []
for i, row in enumerate(raw_grid):
grid.append([])
for j, player in enumerate(row):
# Add a cell dict to the grid (or None for empty spots).
cell = new_cell(i, j, player) if player else None
grid[i].append(cell)
if not cell: continue
# For each direction, look to the previous cell. If it matches the
# current player, we can extend the run in that direction.
for d, offset in offsets.iteritems():
r, c = (i + offset[0], j + offset[1])
if in_bounds(r, c):
prev = grid[r][c]
if prev and prev['player'] == cell['player']:
# We have a match, so the run size is one bigger,
# and we will track that run in the current cell,
# not the previous one.
cell[d] = prev[d] + 1
prev[d] = None
# For all non-None cells, yield run info for any runs that are big enough.
for cell in (c for row in grid for c in row if c):
for d in offsets:
if cell[d] and cell[d] >= run_size:
yield dict(
player = cell['player'],
endpoint = (cell['i'], cell['j']),
direction = d,
run_size = cell[d],
)
main()
输出:
{'player': 1, 'direction': 'h', 'endpoint': (1, 4), 'run_size': 3}
{'player': 2, 'direction': 'f', 'endpoint': (2, 0), 'run_size': 3}
{'player': 2, 'direction': 'h', 'endpoint': (2, 2), 'run_size': 3}
{'player': 1, 'direction': 'b', 'endpoint': (2, 3), 'run_size': 3}
{'player': 1, 'direction': 'f', 'endpoint': (3, 2), 'run_size': 3}
{'player': 1, 'direction': 'v', 'endpoint': (3, 3), 'run_size': 4}
{'player': 2, 'direction': 'v', 'endpoint': (4, 4), 'run_size': 3}