leetcode:79.二维矩阵可以走出的单词

题目描述

给定一个char[][] matrix,也就是char类型的二维数组,再给定一个字符串word,可以从任何一个某个位置出发,可以走上下左右,能不能找到word?

char[][] m = 
{ { 'a', 'b', 'z' },
  { 'c', 'd', 'o' },
  { 'f', 'e', 'o' },

设定1:可以走重复路的情况下,返回能不能找到
比如,word = “zoooz”,是可以找到的,z -> o -> o -> o -> z,因为允许走一条路径中已经走过的字符

设定2:不可以走重复路的情况下,返回能不能找到
比如,word = “zoooz”,是不可以找到的,因为允许走一条路径中已经走过的字符不能重复走

题目解析

样本对应模型

暴力递归

定义 f ( i , j , k ) f(i,j,k) f(ijk)表示:从 i , j i,j ij位置出发,能不能走出str从 k k k开头往后的所有字符

因为可以从任何一个位置出发,所以要对每一个位置都调用 f ( i , j , 0 ) f(i,j,0) f(ij0),只要有任何一个true,答案就是true

可以走重复路

// 从m[i][j]这个字符出发,能不能找到str[k...]这个后缀串
bool canLoop(std::vector<std::vector<char>> m, int i, int j, std::string str, int k){

    //已经走完了
    if(k == str.size()){
        return true;
    }

    //还有字符需要走
    //越界或者m[i][j] != str[k]
    if(i == -1 || i == m.size() || j == -1 || j == m[0].size() || m[i][j] != str[k]){
        return false;
    }

    // 不越界!m[i][j] == str[k] 对的上的!
    // str[k+1....]
    bool ans = false;
    if (canLoop(m, i + 1, j, str, k + 1) || canLoop(m, i - 1, j, str, k + 1) || canLoop(m, i, j + 1, str, k + 1)
        || canLoop(m, i, j - 1, str, k + 1)) {
        ans = true;
    }
    return ans;
}

不可以走重复路

关键在于怎么记住已经走过的路

// 从m[i][j]这个字符出发,能不能找到str[k...]这个后缀串
bool noLoop(std::vector<std::vector<char>> m, int i, int j, std::string str, int k){

    //已经走完了
    if(k == str.size()){
        return true;
    }

    //还有字符需要走
    //越界或者m[i][j] != str[k]
    if(i == -1 || i == m.size() || j == -1 || j == m[0].size() || m[i][j] != str[k]
    || m[i][j] == 0){  //-----------回头路----------
        return false;
    }

    // 不越界!m[i][j] == str[k] 对的上的
    m[i][j] = 0;//标记本条路已经走过
    // str[k+1....]
    bool ans = false;
    if (noLoop(m, i + 1, j, str, k + 1) || noLoop(m, i - 1, j, str, k + 1) || noLoop(m, i, j + 1, str, k + 1)
        || noLoop(m, i, j - 1, str, k + 1)) {
        ans = true;
    }
    m[i][j] = str[k]; //恢复现场(******,以当前点作为起点的路已经尝试完毕了,一定要恢复,否则可能影响其他起点的选择)
    return ans;
}

可以从任何一个位置出发

int ways(std::vector<std::vector<char>> &m, std::string &str){
        if(str.empty()){
            return 0;
        }
        int M = m.size(), N = m[0].size();
        for (int i = 0; i < M; ++i) {
            for (int j = 0; j < N; ++j) {
                int p = xxxxx(i, j, 0, m, str);
                if(p){
                    return true;
                }
            }
        }
        return false;
    }

动态规划

不可以走重复路的不能改成动态规划,因为可变参数不只有i,j,k了,m参与到了可变参数中


bool checkPrevious(std::vector<std::vector<std::vector<bool>>>  dp, int i, int j, int k) {
    bool up = i > 0 ? (dp[i - 1][j][k - 1]) : false;
    bool down = i < dp.size() - 1 ? (dp[i + 1][j][k - 1]) : false;
    bool left = j > 0 ? (dp[i][j - 1][k - 1]) : false;
    bool right = j < dp[0].size() - 1 ? (dp[i][j + 1][k - 1]) : false;
    return up || down || left || right;
}

bool canLoop(std::vector<std::vector<char>> m,  std::string w){
 
    if(w.empty()){
        return true;
    }
    
    if(m.empty() || m[0].empty()){
        return false;
    }
    
    int N = m.size(), M = m[0].size(), len = w.size();
    // dp[i][j][k]表示:必须以m[i][j]这个字符结尾的情况下,能不能找到w[0...k]这个前缀串
    std::vector<std::vector<std::vector<bool>>> dp(N, std::vector<std::vector<bool>>(M, std::vector<bool>(len, false)));
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            dp[i][j][0] = (m[i][j] == w[0]);
        }
    }
    for (int k = 1; k < len; k++) {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                dp[i][j][k] = (m[i][j] == w[k] && checkPrevious(dp, i, j, k));
            }
        }
    }
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (dp[i][j][len - 1]) {
                return true;
            }
        }
    }
    return false;
}

类似题目

题目思路
leetcode:79. 二维矩阵可以走出的单词(可以重复路,不可以重复路)Word Search 回溯,动态规划(不可以走重复路不能改动态规划)
leetcode:212. 二维矩阵可以走出的哪些单词(不可以重复路)Word Search II II回溯 + 字典树
leetcode:208. 实现字典树(前缀树) Implement Trie (Prefix Tree)
leetcode:211. 添加与搜索单词 - 数据结构设计.可以匹配任何一个单词,所以有了.就要搜索26条子路
leetcode:980. 不同路径 III Unique Paths III回溯
1032. 字符流 Stream of Characters
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值