剑指Offer12|LeetCode79.矩阵中的路径

时间:2021-08-02
本文章向大家介绍剑指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
}

原文地址:https://www.cnblogs.com/hzpeng/p/15090900.html