题目描述(中等难度)

79. Word Search - 图1

意思就是从某个字符出发,然后它可以向左向右向上向下移动,走过的路径构成一个字符串,判断是否能走出给定字符串的 word ,还有一个条件就是走过的字符不能够走第二次。

比如 SEE,从第二行最后一列的 S 出发,向下移动,再向左移动,就走出了 SEE。

ABCB,从第一行第一列的 A 出发,向右移动,再向右移动,到达 C 以后,不能向左移动回到 B ,并且也没有其他的路径走出 ABCB 所以返回 false。

解法一 DFS

我们可以把矩阵看做一个图,然后利用图的深度优先遍历 DFS 的思想就可以了。

79. Word Search - 图2

我们需要做的就是,在深度优先遍历过程中,判断当前遍历元素是否对应 word 元素,如果不匹配就结束当前的遍历,返回上一次的元素,尝试其他路径。当然,和普通的 dfs 一样,我们需要一个 visited 数组标记元素是否访问过。

  1. public boolean exist(char[][] board, String word) {
  2. int rows = board.length;
  3. if (rows == 0) {
  4. return false;
  5. }
  6. int cols = board[0].length;
  7. boolean[][] visited = new boolean[rows][cols];
  8. //从不同位置开始
  9. for (int i = 0; i < rows; i++) {
  10. for (int j = 0; j < cols; j++) {
  11. //从当前位置开始符合就返回 true
  12. if (existRecursive(board, i, j, word, 0, visited)) {
  13. return true;
  14. }
  15. }
  16. }
  17. return false;
  18. }
  19. private boolean existRecursive(char[][] board, int row, int col, String word, int index, boolean[][] visited) {
  20. //判断是否越界
  21. if (row < 0 || row >= board.length || col < 0 || col >= board[0].length) {
  22. return false;
  23. }
  24. //当前元素访问过或者和当前 word 不匹配返回 false
  25. if (visited[row][col] || board[row][col] != word.charAt(index)) {
  26. return false;
  27. }
  28. //已经匹配到了最后一个字母,返回 true
  29. if (index == word.length() - 1) {
  30. return true;
  31. }
  32. //将当前位置标记位已访问
  33. visited[row][col] = true;
  34. //对四个位置分别进行尝试
  35. boolean up = existRecursive(board, row - 1, col, word, index + 1, visited);
  36. if (up) {
  37. return true;
  38. }
  39. boolean down = existRecursive(board, row + 1, col, word, index + 1, visited);
  40. if (down) {
  41. return true;
  42. }
  43. boolean left = existRecursive(board, row, col - 1, word, index + 1, visited);
  44. if (left) {
  45. return true;
  46. }
  47. boolean right = existRecursive(board, row, col + 1, word, index + 1, visited);
  48. if (right) {
  49. return true;
  50. }
  51. //当前位置没有选进来,恢复标记为 false
  52. visited[row][col] = false;
  53. return false;
  54. }

我们可以优化一下空间复杂度,我们之前是用了一个等大的二维数组来标记是否访问过。其实我们完全可以用之前的 board,我们把当前访问的元素置为 “$” ,也就是一个在 board 中不会出现的字符。然后当上下左右全部尝试完之后,我们再把当前元素还原就可以了。

  1. public boolean exist(char[][] board, String word) {
  2. int rows = board.length;
  3. if (rows == 0) {
  4. return false;
  5. }
  6. int cols = board[0].length;
  7. for (int i = 0; i < rows; i++) {
  8. for (int j = 0; j < cols; j++) {
  9. if (existRecursive(board, i, j, word, 0)) {
  10. return true;
  11. }
  12. }
  13. }
  14. return false;
  15. }
  16. private boolean existRecursive(char[][] board, int row, int col, String word, int index) {
  17. if (row < 0 || row >= board.length || col < 0 || col >= board[0].length) {
  18. return false;
  19. }
  20. if (board[row][col] != word.charAt(index)) {
  21. return false;
  22. }
  23. if (index == word.length() - 1) {
  24. return true;
  25. }
  26. /*********************改变的地方****************************************/
  27. char temp = board[row][col];
  28. board[row][col] = '$';
  29. /*********************************************************************/
  30. boolean up = existRecursive(board, row - 1, col, word, index + 1);
  31. if (up) {
  32. return true;
  33. }
  34. boolean down = existRecursive(board, row + 1, col, word, index + 1);
  35. if (down) {
  36. return true;
  37. }
  38. boolean left = existRecursive(board, row, col - 1, word, index + 1);
  39. if (left) {
  40. return true;
  41. }
  42. boolean right = existRecursive(board, row, col + 1, word, index + 1);
  43. if (right) {
  44. return true;
  45. }
  46. /*********************改变的地方****************************************/
  47. board[row][col] = temp;
  48. /*********************************************************************/
  49. return false;
  50. }

这里,看到另外一种标记和还原的方法。异或。

  1. /*********************之前的做法****************************************/
  2. char temp = board[row][col];
  3. board[row][col] = '$';
  4. /*********************************************************************/
  5. /*********************利用异或****************************************/
  6. board[row][col] ^= 128;
  7. /*********************************************************************/
  8. //还原
  9. /********************之前的做法****************************************/
  10. board[row][col] = temp;
  11. /*********************************************************************/
  12. /*********************利用异或****************************************/
  13. board[row][col] ^= 128;
  14. /*********************************************************************/

至于原理,因为 ASCII 码值的范围是 0 - 127,二进制的话就是 0000 0000 - 0111 1111,我们把它和 128 做异或,也就是和 1000 0000 。这样,如果想还原原来的数字只需要再异或 128 就可以了。

其实原理是一样的,都是把之前的数字变成当前 board 不存在的字符,然后再变回来。只不过这里考虑它的二进制编码,在保留原有信息的基础上做改变,不再需要 temp 变量。

关键是对题目的理解,抽象到 DFS,题目就迎刃而解了。异或的应用很强。

windliang wechat

添加好友一起进步~

如果觉得有帮助的话,可以点击 这里 给一个 star 哦 ^^

如果想系统的学习数据结构和算法,强烈推荐一个我之前学过的课程,可以点击 这里 查看详情