79. Word Search

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		backtracking(board, word, 0, 0);
		return res;
	}
private:
	string track;
	bool res = false;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		if (track == word || res) {
			res = true;
			return;
		}
		for (int i = row; i < board.size(); i++) {
			for (int j = col; j < board[i].size(); j++) {
				auto directions = get_next_directions(board, i, j);
				if (directions.size() == 0) {
					return;
				}
				for (auto d: directions) {
					int r, c;
					track.push_back(board[i][j]);
					std::tie(r, c) = next_char_pos(board, row, col, d);
					if (word.find(track) == 0) {
						backtracking(board, word, r, c);
					}
					track.pop_back();
				}

			}
		}
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

失败的用例:

[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
"ABCB"

因为方向往回走导致,应该规避方向往回走。通过一个 set 来跟踪位置,防止进入前面的位置:

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		backtracking(board, word, 0, 0);
		return res;
	}
private:
	string track;
	bool res = false;
	set<tuple<int, int>> pos;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		if (track == word || res) {
			res = true;
			return;
		}
		for (int i = row; i < board.size(); i++) {
			for (int j = col; j < board[i].size(); j++) {
				auto directions = get_next_directions(board, i, j);
				if (directions.size() == 0) {
					return;
				}
				for (auto d: directions) {
					tuple<int, int> p (i, j);
					if (pos.find(p) != pos.end()) {
						continue;
					}
					pos.insert(p);
					track.push_back(board[i][j]);

					int r, c;
					std::tie(r, c) = next_char_pos(board, row, col, d);


					if (word.find(track) == 0) {
						backtracking(board, word, r, c);
					}
					track.pop_back();
					pos.erase(p);
				}

			}
		}
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

失败的用例:

[["a"]]
"a"

代码逻辑有问题,修正后:

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		backtracking(board, word, 0, 0);
		return res;
	}
private:
	string track;
	bool res = false;
	set<tuple<int, int>> pos;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		if (track == word || res) {
			res = true;
			return;
		}
		for (int i = row; i < board.size(); i++) {
			for (int j = col; j < board[i].size(); j++) {
				tuple<int, int> p (i, j);
				if (pos.find(p) != pos.end()) {
					continue;
				}
				pos.insert(p);
				track.push_back(board[i][j]);
				if (track == word || res) {
					res = true;
					return;
				}
				if (word.find(track) == 0) {
					auto directions = get_next_directions(board, i, j);
					for (auto d: directions) {
						int r, c;
						std::tie(r, c) = next_char_pos(board, row, col, d);
						backtracking(board, word, r, c);
					}
				}

				track.pop_back();
				pos.erase(p);

			}
		}
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

以下测试用例无法通过:

[["a","b"]]
"ba"

调整代码后(进入下一个节点的 row/col 没用 i/j):

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		backtracking(board, word, 0, 0);
		return res;
	}
private:
	string track;
	bool res = false;
	set<tuple<int, int>> pos;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		cout << track << endl;
		if (track == word || res) {
			res = true;
			return;
		}
		for (int i = row; i < board.size(); i++) {
			for (int j = col; j < board[i].size(); j++) {
				tuple<int, int> p (i, j);
				if (pos.find(p) != pos.end()) {
					continue;
				}
				pos.insert(p);
				track.push_back(board[i][j]);
				if (track == word) {
					res = true;
					return;
				}
				if (word.find(track) == 0) {
					auto directions = get_next_directions(board, i, j);
					for (auto d: directions) {
						int r, c;
						std::tie(r, c) = next_char_pos(board, i, j, d);
						backtracking(board, word, r, c);
					}
				}

				track.pop_back();
				pos.erase(p);
			}
		}
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

此时的失败用例:

[["a","b"],["c","d"]]
"abcd"

改成如果位置已经存在则返回可以通过:

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		backtracking(board, word, 0, 0);
		return res;
	}
private:
	string track;
	bool res = false;
	set<tuple<int, int>> pos;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		if (track == word || res) {
			res = true;
			return;
		}
		for (int i = row; i < board.size(); i++) {
			for (int j = col; j < board[i].size(); j++) {
				tuple<int, int> p (i, j);
				if (pos.find(p) != pos.end()) {
					return;  // <--------- here
				}
				pos.insert(p);
				track.push_back(board[i][j]);
				if (word.find(track) == 0) {
					auto directions = get_next_directions(board, i, j);
					if (track == word) {
						res = true;
						return;
					}
					for (auto d: directions) {
						int r, c;
						std::tie(r, c) = next_char_pos(board, i, j, d);
						backtracking(board, word, r, c);
					}
				}

				track.pop_back();
				pos.erase(p);
			}
		}
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

但是在下面测试用例失败了:

[["b"],["a"],["b"],["b"],["a"]]
"baa"

简单的过滤掉:

if (board.size() > 0 && board[0].size() == 1) {
	string s;
	for (int i = 0; i < board.size(); i++) {
		s.push_back(board[0][0]);
	}
	return s.find(word) != string::npos;
}

现在下面失败了:

[["a","a","b","a","a","b"],["a","a","b","b","b","a"],["a","a","a","a","b","a"],["b","a","b","b","a","b"],["a","b","b","a","b","a"],["b","a","a","a","a","b"]]
"bbbaabbbbbab"

经过仔细推敲后:

class Solution {
public:
	bool exist(vector<vector<char>>& board, string word) {
		for (int i = 0; i < board.size(); i++) {
			for (int j = 0; j < board[i].size(); j++) {
				backtracking(board, word, i, j);
			}
		}
		return res;
	}
private:
	string track;
	bool res = false;
	set<tuple<int, int>> pos;

	enum Direction {
		right,
		down,
		up,
		left,
	};
	/**
	 * @param dir: 0: right, 1: down, 2: up, 3: left
	 */
	void backtracking(vector<vector<char>>& board, string word, int row, int col) {
		tuple<int, int> p (row, col);
		if (pos.find(p) != pos.end()) {
			return;  // <--------- here
		}
		pos.insert(p);
		track.push_back(board[row][col]);
		if (track == word) {
			cout << track << endl;
			res = true;
			return;
		}
		if (res) {
			return;
		}
		if (word.find(track) == 0) {
			cout << row << ", " << col << endl;
			auto directions = get_next_directions(board, row, col);
			for (auto d: directions) {
				int r, c;
				std::tie(r, c) = next_char_pos(board, row, col, d);
				cout << "  " << r << "," << c << endl;
				backtracking(board, word, r, c);
			}
		}
		track.pop_back();
		pos.erase(p);
	}

	vector<Direction> get_next_directions(vector<vector<char>>& board, int row, int col) {
		vector<Direction> r;
		if (col < board[row].size() - 1) {
			r.push_back(right);
		}
		if (row < board.size() - 1) {
			r.push_back(down);
		}

		if (row > 0) {
			r.push_back(up);
		}
		if (col > 0) {
			r.push_back(left);
		}
		return r;
	}

	tuple<int, int> next_char_pos(vector<vector<char>>& board, int row, int col, Direction dir) {
		switch(dir) {
			case right:
				col++;
				break;
			case left:
				col--;
				break;
			case up:
				row--;
				break;
			case down:
				row++;
				break;
		}
		tuple<int, int>r(row, col);
		return r;
	}
};

下面用例会导致处理事件过长:

[["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"]]
"AAAAAAAAAAAAAAB"

测试用例快照:

[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
"ABCCED"
[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
"SEE"
[["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
"ABCB"
[["a"]]
"a"
[["a","b"]]
"ba"
[["a","b"],["c","d"]]
"dbac"
[["a","b"],["c","d"]]
"abcd"
[["a","a","a","a"],["a","a","a","a"],["a","a","a","a"]]
"aaaaaaaaaaaaa"
[["b"],["a"],["b"],["b"],["a"]]
"baa"
[["a"]]
"ab"
[["a","a","b","a","a","b"],["a","a","b","b","b","a"],["a","a","a","a","b","a"],["b","a","b","b","a","b"],["a","b","b","a","b","a"],["b","a","a","a","a","b"]]
"bbbaabbbbbab"
[["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"],["A","A","A","A","A","A"]]
"AAAAAAAAAAAAAAB"