记一次成功的探索过程:算法题-单词搜索II (暴力+字典树)

题目

给定一个 m x n 二维字符网格board和一个单词(字符串)列表 words,找出所有同时在二维网格和字典中出现的单词。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例一

输入:board = [[“o”,“a”,“a”,“n”],[“e”,“t”,“a”,“e”],[“i”,“h”,“k”,“r”],[“i”,“f”,“l”,“v”]], words = [“oath”,“pea”,“eat”,“rain”]
输出:[“eat”,“oath”]

示例二

输入:board = [[“a”,“b”],[“c”,“d”]], words = [“abcb”]
输出:[]


解题

思路一:暴力求解

刚开始没思路,看题中数据量不是特别大,想直接暴力解了:每个单词,从每个节点开始向四周遍历,找到就返回;但是还是超时了

    int[] nm = {-1, 1, 0, 0};
    int[] nn = {0, 0, -1, 1};
    public List<String> findWords(char[][] board, String[] words) {
        int m = board.length, n = board[0].length;
        List<String> ans = new ArrayList<>();
        for (String word : words) {
            if (word.length() > m * n) continue;
            boolean[][] isUsed = new boolean[m][n];
            boolean flag = false;
            for (int i = 0; i < m; i ++) {
                if (flag) {
                    break;
                }
                for (int j = 0; j < n; j ++) {
                    if (findWordsCore(board, word, isUsed, 0, i, j)) {
                        ans.add(word);
                        flag = true;
                        break;
                    }
                }
            }
        }
        return ans;
    }

    private boolean findWordsCore(char[][] board, String word, boolean[][] isUsed, int curIndex, int m, int n) {
        if (curIndex >= word.length()) {
            return true;
        }
        if (m < 0 || m >= board.length || n < 0 || n >= board[0].length || isUsed[m][n]
                || word.charAt(curIndex) != board[m][n]) {
            return false;
        }
        isUsed[m][n] = true;
        for (int i = 0; i < 4; i ++) {
            int newM = m + nm[i], newN = n + nn[i];
            if (findWordsCore(board, word, isUsed, curIndex + 1, newM, newN)) {
                return true;
            }
        }
        isUsed[m][n] = false;
        return false;
    }
思路二:字典树

修改一下思路:由每个单词去循环遍历矩阵,改为循环遍历矩阵时找是否有匹配的单词 ;
单词的保存用字典树,字典树里的节点里也保存一下完整单词val。
找到单词后,将字典树中表示单词的isEnd置为false,防止多次遍历到,重复添加

    int[] nm = {-1, 1, 0, 0};
    int[] nn = {0, 0, -1, 1};
    public List<String> findWords(char[][] board, String[] words) {
        int m = board.length, n = board[0].length;
        List<String> ans = new ArrayList<>();
        TrieTree trieTree = new TrieTree();
        for (String word : words) {
            trieTree.insert(word);
        }
        boolean[][] isUsed = new boolean[m][n];
        for (int i = 0; i < m; i ++) {
            for (int j = 0; j < n; j ++) {
                findWordsCore(board, isUsed, i, j, trieTree.root, ans);
            }
        }
        return ans;
    }

    private void findWordsCore(char[][] board, boolean[][] isUsed, int m, int n, TrieNode node, List<String> ans) {
        if (m < 0 || m >= board.length || n < 0 || n >= board[0].length || isUsed[m][n]) {
            return;
        }
        char c = board[m][n];
        if (node.nodes[c-'a'] == null) {
            return;
        }
        node = node.nodes[c-'a'];
        if (node.isEnd) {
            ans.add(node.val);
            node.isEnd = false;
        }
        isUsed[m][n] = true;
        for (int i = 0; i < 4; i ++) {
            int newM = m + nm[i], newN = n + nn[i];
            findWordsCore(board, isUsed, newM, newN, node, ans);
        }
        isUsed[m][n] = false;
    }

    class TrieNode {
        String val;
        boolean isEnd;
        TrieNode[] nodes;
        TrieNode() {
            nodes = new TrieNode[26];
        }
    }

    class TrieTree {
        TrieNode root = new TrieNode();

        public void insert(String word) {
            if (word == null || word.length() == 0) {
                return;
            }
            TrieNode node = root;
            for (int i = 0; i < word.length(); i ++) {
                char c = word.charAt(i);
                if (node.nodes[c-'a'] == null) {
                    node.nodes[c-'a'] = new TrieNode();
                }
                node = node.nodes[c-'a'];
            }
            node.isEnd = true;
            node.val = word;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值