---
# System prepended metadata

title: '[競程] Where''s Waldorf?'
tags: [競程]

---

# [競程] Where's Waldorf?

###### tags: `競程`

* Online Judge: [10010](https://onlinejudge.org/index.php?option=onlinejudge&Itemid=8&page=show_problem&problem=951)

## 題目
以字串格式給定一個 $m\times n$ 的圖`grid`，每個格子上都有一個字母，大小寫混雜。再給定一個陣列`words`，包含數個字串（忽視大小寫，且保證一定存在圖中），要問這些字串在圖中的分別的起始位置座標（由1開始）。

這些字串可以在圖中可以有八種方向 : 往左、往右、往上、往下、往左上、往左下、往右上、往右下。

## 範例
```
abcDEFGhigg
hEbkWalDork
FtyAwaldORm
FtsimrLqsrc
byoArBeDeyv
Klcbqwikomk
strEBGadhrb
yUiqlxcnBjf
```

```
words = ["Waldorf", "Bambi", "Betty", "Dagbert"]
```


輸出的答案會是：
```
[[2, 5], [2, 3], [1, 2], [7, 8]]
```


## 思路

這題需要用類似DFS的概念去解決，然而一旦確定了其中一個方向之後，就只能一直持續往相同的方向搜尋，所以需要把一般的尋訪修正一下。事實上比真正的DFS簡單。

在開始編寫程式之前，我們可以先想想，用肉眼直接在圖上面找單字的時候我們會在腦海中建立怎樣的程序，再將這個程序編寫成程式。通常這種方法可以建立出不錯的演算法，再經過些許優化，可能就會是最佳解了。

另外，有以下幾點要注意：
* 題目給定的`grid`是一個字串，可以先轉成list方便做尋訪
* 因為大小寫不拘，所以我們可以把全部的字母都轉換成小寫（或大寫，開心就好，甚至可以是`int`）
* index的計算是由1開始，所以要記得在最後給出的答案上加1
* 若有多個解時，回傳最靠近左上角的解
## 解答
首先我們要對輸入資料進行處理。在這邊我們把所有的字母使用`.lower()`轉換成小寫字元，再使用`ord`函式獲取字母對應的整數編號（例如`ord("a")`是97）：
```python=
grid = [[ord(c.lower()) for c in row] for row in grid.split("\n")]
words = [[ord(c.lower()) for c in w] for w in words]
```
轉成整數的理由是一個整數占用的記憶體空間較小，比較整數也比比較字元快速。

接著我們定義一個`valid_index`函式來檢查一組給定的座標`x, y`是不是在題目給定的圖上合法的座標：
```python=
def valid_index(x, y):
    if (x < 0) or (y < 0) or (x >= m) or (y >= n):
        return False
    return True
```

然後再定義一個`find(x, y, word)`函式，用來檢查word是不是以`x, y`為起始點在這張圖上：
```python=
dirs = [[0, 1], [1, 0], [0, -1], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]

def find(x, y, word):
    if word[0] != grid[x][y]:
        return False
    for dx, dy in dirs:
        end = True
        for step in range(1, len(word)):
            curr_x = x + dx * step
            curr_y = y + dy * step
            if (not valid_index(curr_x, curr_y)) or (grid[curr_x][curr_y] != word[step]):
                end = False
                break
        if end:
            return True
    return False
```
在這邊我們先判斷首字母相不相等，以去除大部分不符合的情況。接著我們對八個方向進行迭代。對於每一個可能的方向，我們初始化一個變數`end = True`，如果那個方向走了`step`步，出現不匹配的狀況（`grid[curr_x][curr_y] != word[step]`），或是走到圖的盡頭（`not valid_index(curr_x, curr_y`），就停止往那個方向繼續走，換下一個方向。如果某個特定方向走完了，沒有出現不匹配，那就代表找到了那個字串，直接`return True`。最後，如果每個方向都沒有找到字串，那就是沒有在這點上找到字串了，所以`return False`。

現在我們已經寫好單點的尋訪演算法了，只要對圖上每個點和每個字串執行`find`函式，並把結果收集起來即可：
```python=
result = []

for word in words:
    found = False
    for x in range(m):
        for y in range(n):
            found = find(x, y, word)
            if found:
                result.append([x + 1, y + 1])
                break
        if found:
            break
```
注意這邊的迴圈流程控制，我們使用了一個變數`found`以在找到的狀況跳出對座標的迭代，因為題目要求如果在一張圖上有多個解時，回傳最靠近左上角的，也就是找到的第一個解。


:::spoiler 完整程式碼如下
```python=
def find_words(grid, words):
    grid = [[ord(c.lower()) for c in row] for row in grid.split("\n")]
    words = [[ord(c) for c in w.lower()] for w in words]

    m = len(grid)
    n = len(grid[0])

    def valid_index(x, y):
        if (x < 0) or (y < 0) or (x >= m) or (y >= n):
            return False
        return True

    dirs = [[0, 1], [1, 0], [0, -1], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]

    def find(x, y, word):
        if word[0] != grid[x][y]:
            return False
        for dx, dy in dirs:
            end = True
            for step in range(1, len(word)):
                curr_x = x + dx * step
                curr_y = y + dy * step
                if (not valid_index(curr_x, curr_y)) or (grid[curr_x][curr_y] != word[step]):
                    end = False
                    break
            if end:
                return True
        return False

    result = []

    for word in words:
        found = False
        for x in range(m):
            for y in range(n):
                found = find(x, y, word)
                if found:
                    result.append([x + 1, y + 1])
                    break
            if found:
                break

    return result



grid = "\
abcDEFGhigg\n\
hEbkWalDork\n\
FtyAwaldORm\n\
FtsimrLqsrc\n\
byoArBeDeyv\n\
Klcbqwikomk\n\
strEBGadhrb\n\
yUiqlxcnBjf\
"

words = ["Waldorf", "Bambi", "Betty", "Dagbert"]

print(find_words(grid, words))
:::
