Leetcode 之 Word Search

来源:互联网 发布:淘宝宝贝模板 编辑:程序博客网 时间:2024/05/21 06:20

Given a 2D board and a word, find if the word exists in the grid.

The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.

For example,
Given board =

[ [“ABCE”],
[“SFCS”],
[“ADEE”]]

word = “ABCCED”, -> returns true,
word = “SEE”, -> returns true,
word = “ABCB”, -> returns false.

实际上是一个回溯中的迷宫问题。先总结一下回溯吧。

回溯(backtracking)是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。
下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图(迷宫问题)或树(N皇后问题)。
一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。

回溯方法的步骤如下:
1) 定义一个解空间,它包含问题的解。
2) 用适于搜索的方式组织该空间。
3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。

回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。

回溯的主要应用就是组合问题和迷宫问题。先说一下迷宫问题。

计算机解迷宫时,通常用的是”试探和回溯”的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止,如果所有可能的通路都试探过,还是不能走到终点,那就说明该迷宫不存在从起点到终点的通道。

  1.从入口进入迷宫之后,不管在迷宫的哪一个位置上,都是先往东走,如果走得通就继续往东走,如果在某个位置上往东走不通的话,就依次试探往南、往西和往北方向,从一个走得通的方向继续往前直到出口为止;
  2.如果在某个位置上四个方向都走不通的话,就退回到前一个位置,换一个方向再试,如果这个位置已经没有方向可试了就再退一步,如果所有已经走过的位置的四个方向都试探过了,一直退到起始点都没有走通,那就说明这个迷宫根本不通;
 3.所谓”走不通”不单是指遇到”墙挡路”,还有”已经走过的路不能重复走第二次”,它包括”曾经走过而没有走通的路”。显然为了保证在任何位置上都能沿原路退回,需要用一个”后进先出”的结构即栈来保存从入口到当前位置的路径。并且在走出出口之后,栈中保存的正是一条从入口到出口的路径。
 
由此,求迷宫中一条路径的算法的基本思想是:
若当前位置”可通”,则纳入”当前路径”,并继续朝”下一位置”探索;若当前位置”不可通”,则应顺着”来的方向”退回到”前一通道块”,然后朝着除”来向”之外的其他方向继续探索;若该通道块的四周四个方块均”不可通”,则应从”当前路径”上删除该通道块。

设定当前位置的初值为入口位置;  do{    若当前位置可通,    则{     将当前位置插入栈顶;       // 纳入路径     若该位置是出口位置,则算法结束;      // 此时栈中存放的是一条从入口位置到出口位置的路径     否则切换当前位置的东邻方块为新的当前位置;     }    否则    {    若栈不空且栈顶位置尚有其他方向未被探索,    则设定新的当前位置为: 沿顺时针方向旋转找到的栈顶位置的下一相邻块;    若栈不空但栈顶位置的四周均不可通,    则{ 删去栈顶位置;         // 从路径中删去该通道块      若栈不空,则重新测试新的栈顶位置,      直至找到一个可通的相邻块或出栈至栈空;     }   }} while (栈不空);

对于我们这道题来说,就是把单词的每一位和板上的依次比对。
当找到某一位时,将单词的下一位与该坐标的上下左右进行递归比较,如果有一个方向有,接着找单词的下一位;反之,则重新回到单词的首位进行比较。

为了避免死循环,我们先在整个板上找单词的首位,把找到的位置作为递归的开始。

这里要注意几个终止条件:
1、单词最后一位已经找完,说明找到了,要终止;
2、上下左右坐标越界了,要终止;
3、某一个坐标已经找过了(visited数组这一位为1),要终止。

还有避免TLE的条件:
1、在找四个方向的时候,在一个方向找到了就及时返回;
2、只用一个visited数组,如果在该位置上找到了置为1, 如果四个方向都没有找到它的下一位的话,把这一位置回0。

代码如下:

public boolean exist(char[][] board, String word) {       int row = board.length;         int col = board[0].length;         char[] wordChar = word.toCharArray();          flag = new int[row][col];         for(int i = 0;i<board.length;i++){             for(int j = 0;j< board[0].length;j++){                       if(findchar(0, wordChar, board, i, j)){                     return true;                 }             }         }         return false;     }public boolean findchar(int ch, char[] word , char[][] board, int i, int j){         if(ch == word.length) return true;         else if(j >= board[0].length || i >= board.length || i< 0 || j < 0) {             return false;            }         else if(flag[i][j] != 0){             return false;         }         else{             boolean right = false;             boolean left = false;             boolean up = false;             boolean down = false;             if(board[i][j] == word[ch]){                 flag[i][j] = 1;                 right = findchar(ch+1, word, board, i + 1, j);                 if(right) return right;                 left = findchar(ch+1, word, board, i - 1, j);                 if(left) return left;                 up = findchar(ch+1, word, board, i, j + 1);                 if(up) return up;                 down = findchar(ch+1, word, board, i, j - 1);                 if(!down) flag[i][j] = 0;                 return down;                         }             else{                 return false;                       }         }     }}

83 / 83 test cases passed.
Status: Accepted
Runtime: 364 ms

0 0
原创粉丝点击