1. 程式人生 > 其它 >劍指Offer12|LeetCode79.矩陣中的路徑

劍指Offer12|LeetCode79.矩陣中的路徑

題目

給定一個m x n 二維字元網格board 和一個字串單詞word 。如果word 存在於網格中,返回 true ;否則,返回 false 。

單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母不允許被重複使用。

示例 1:

輸入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
輸出:true

示例 2:

輸入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
輸出:true

示例 3:

輸入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
輸出:false


提示:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 僅由大小寫英文字母組成

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/word-search
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

解題方法

DFS回溯

1.雙重迴圈找到字串的起點
2.基於起點上下左右四個方向找剩餘路徑
3.在找的過程中判斷選擇是否正確,是否越界,是否已訪問過
4.宣告一個變數儲存訪問過的元素
5.如果遞迴下一個節點時錯誤的,說明當前選擇也是錯誤的,需要把當前元素的是否訪問狀態改回未訪問

時間複雜度O(MN*3^L) M,N為網格的長寬 L為字串長度 每次遞迴除了第一次可以進入4個分支,
其餘最多進入3個分支,因為每個位置只能一次,走過無法再訪問。
空間複雜度O(MN) 開闢儲存是否訪問過的陣列
如果採用修改原陣列的方法 空間複雜度O(1)

程式碼

// dfs + 輔助陣列
func exist(board [][]byte, word string) bool {
	m,n := len(board),len(board[0])
	// 訪問記錄
	visited := make([][]bool,m)
	for i := 0;i < m;i++{
		visited[i] = make([]bool,n)
	}

	var canFind func(r,c,i int) bool
	canFind = func(r, c, i int) bool {
		// 已經找到複合字符串長度的路徑
		if i == len(word){
			return true
		}
		// 索引越界
		if r < 0 || r >= m || c < 0 || c >= n{
			return false
		}
		// 當前元素已訪問過或者當前元素不等於當前需要找的字元
		if visited[r][c] || board[r][c] != word[i] {
			return false
		}
		// 設定當前元素為已訪問狀態
		visited[r][c] =  true
		// 遞迴剩餘路徑 在 || 判斷中,只要有符合的便不會繼續執行後面的判斷,達到剪枝的目的
		if canFind(r+1,c,i+1) || canFind(r-1,c,i+1) || canFind(r,c+1,i+1) || canFind(r,c-1,i+1){
			return true
		}
		// 沒有符合的路徑 將當前元素訪問狀態回退
		visited[r][c] = false
		return false
	}

	for i := 0;i < m;i++ {
		for j := 0;j < n;j++{
			if board[i][j] == word[0] && canFind(i,j,0){
				return true
			}
		}
	}
	return false
}

// dfs + 直接修改原陣列
func exist2(board [][]byte, word string) bool {
	m,n := len(board),len(board[0])
	var canFind func(r,c,i int) bool
	canFind = func(r, c, i int) bool {
		// 已經找到複合字符串長度的路徑
		if i == len(word){
			return true
		}
		// 索引越界
		if r < 0 || r >= m || c < 0 || c >= n{
			return false
		}
		// 如果當前元素不等於尋找元素
		if board[r][c] != word[i] {
			return false
		}
		// 修改元素,表示已訪問
		temp := board[r][c]
		board[r][c] =  ' '
		// 遞迴剩餘路徑 在 || 判斷中,只要有符合的便不會繼續執行後面的判斷,達到剪枝的目的
		if canFind(r+1,c,i+1) || canFind(r-1,c,i+1) || canFind(r,c+1,i+1) || canFind(r,c-1,i+1){
			return true
		}
		// 沒有符合的路徑 將當前元素回退
		board[r][c] =  temp
		return false
	}

	for i := 0;i < m;i++ {
		for j := 0;j < n;j++{
			if board[i][j] == word[0] && canFind(i,j,0){
				return true
			}
		}
	}
	return false
}