力扣DAY60-61 | 热100 | 回溯:单词搜索、分割回文串

前言

中等 √ 继续回溯,不知咋地感觉这两题有点难度,是因为隔一天就手感生疏了吗?

单词搜索

我的题解

定义方向数组、二维访问数组。图搜索,向上下左右每个方向搜索,需要更新的信息:坐标、是否遍历过、搜索到的字母位置。剪枝条件:数组越界、超出单词长度、已搜到、位置已访问过。注意:起始位置不能从0,0开始,需要先遍历整个图找出单词首字母位置,存入队列,再遍历该队列进行搜索。

cpp 复制代码
class Solution {
public:

    int m, n, l;
    bool ans = false;
    vector<vector<int>> dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    vector<vector<bool>> visited;

    void findans(vector<vector<char>>& board, string word, int c, int r, int point){
        
        //数组越界、超出单词长度、不符合
        if (c < 0 || c >= m || r < 0 || r >= n || point >= l || ans == true || visited[c][r] == true)
            return;
        
        for (int i = 0; i < 4; i++){
            if (board[c][r] == word[point]){
                if (point == l-1) ans = true;
                int dc = dir[i][0];
                int dr = dir[i][1];
                visited[c][r] = true;
                findans(board, word, c + dc, r + dr, point + 1);
                visited[c][r] = false;
            }
        }
    }


    bool exist(vector<vector<char>>& board, string word) {
        m = board.size();
        n = board[0].size();
        l = word.size();
        queue<pair<int, int>> q;
        for (int i = 0; i < m; i++){
            for (int j = 0; j < n; j++){
                if (board[i][j] == word[0])
                    q.push({i, j});
            }
        }
        while (!q.empty() && ans == false){
            visited.resize(m, vector<bool>(n, false));
            findans(board, word, q.front().first, q.front().second, 0);
            q.pop();

        }
        return ans;
    }
};

官方题解

官解思路与笔者一致,不赘述,这里贴一个评论区的优化方法

第一个优化

比如示例 3,word=ABCB,其中字母 B 出现了 2 次,但 board 中只有 1 个字母 B,所以肯定搜不到 word,直接返回 false。

一般地,如果 word 的某个字母的出现次数,比 board 中的这个字母的出现次数还要多,可以直接返回 false。

第二个优化

设 word 的第一个字母在 board 中的出现了 x 次,word 的最后一个字母在 board 中的出现了 y 次。

如果 y<x,我们可以把 word 反转,相当于从 word 的最后一个字母开始搜索,这样更多的时候一开始就匹配失败,递归的总次数更少。

加上这两个优化,就可以击败接近 100% 了!

心得

本质是图搜索,知识点都是关联的,比较多边界条件要考虑,另外笔者的剪枝位置与官解不太一致,一个是函数开头,一个是循环里面下一层递归前,应该都是一样的,不过感觉可以的话尽量在下一层递归前剪枝好一点,这应该也是预剪枝和后剪枝的区别?节省时间和空间。

分割回文串

我的题解

遍历从上一刀(初始为0)到末尾的每个位置,如果为回文,加入子解集中,一直到上一刀到末尾为止,加入总解集中。双指针判断是否回文。

cpp 复制代码
class Solution {
public:

    vector<vector<string>> ans;
    vector<string> pre;

    bool isPaindrome(string s, int begin, int end){
        while (begin < end){
            if (s[begin] == s[end]){
                begin ++;
                end --;
            }else{
                return false;
            }
        }
        return true;
    }

    void findans(string s, int point){
        if (point == s.size()){
            ans.push_back(pre);
        }

        for (int i = point; i < s.size(); i++){
            if (isPaindrome(s, point, i)){
                pre.push_back(s.substr(point, i+1-point));
                findans(s, i+1);
                pre.pop_back();
            }
        }
    }

    vector<vector<string>> partition(string s) {
        
        if (s.empty())
            return {};
        
        findans(s, 0);
        return ans;
    }
};

官方题解

官解与笔者思路大致一样,但是用了动态规划把每个子串是不是回文串预处理了出来,大大节省时间。

cpp 复制代码
class Solution {
private:
    vector<vector<int>> f;
    vector<vector<string>> ret;
    vector<string> ans;
    int n;

public:
    void dfs(const string& s, int i) {
        if (i == n) {
            ret.push_back(ans);
            return;
        }
        for (int j = i; j < n; ++j) {
            if (f[i][j]) {
                ans.push_back(s.substr(i, j - i + 1));
                dfs(s, j + 1);
                ans.pop_back();
            }
        }
    }

    vector<vector<string>> partition(string s) {
        n = s.size();
        f.assign(n, vector<int>(n, true));

        for (int i = n - 1; i >= 0; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = (s[i] == s[j]) && f[i + 1][j - 1];
            }
        }

        dfs(s, 0);
        return ret;
    }
};

心得

果然一天不做就手生了,官解的动态规划解法认真学习,有点意思!

知识点

初始化二维数组 f.assign(n, vector<int>(n, true));

  1. vector<int>(n, true)​:

    • 创建一个大小为 nvector<int>(整数向量)
    • 所有元素初始化为 true(注意:true 会被隐式转换为 int 类型的 1
  2. f.assign(n, ...)​:

    • 对向量 f 进行赋值操作
    • f 设置为包含 n 个上述创建的 vector<int>(n, true)
    • 结果是创建一个 n × n 的二维向量,所有元素初始化为 1
相关推荐
沐怡旸28 分钟前
【算法】【链表】328.奇偶链表--通俗讲解
算法·面试
掘金安东尼4 小时前
Amazon Lambda + API Gateway 实战,无服务器架构入门
算法·架构
码流之上4 小时前
【一看就会一写就废 指间算法】设计电子表格 —— 哈希表、字符串处理
javascript·算法
快手技术6 小时前
快手提出端到端生成式搜索框架 OneSearch,让搜索“一步到位”!
算法
CoovallyAIHub1 天前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
NAGNIP1 天前
Serverless 架构下的大模型框架落地实践
算法·架构
moonlifesudo1 天前
半开区间和开区间的两个二分模版
算法
moonlifesudo1 天前
300:最长递增子序列
算法
CoovallyAIHub1 天前
港大&字节重磅发布DanceGRPO:突破视觉生成RLHF瓶颈,多项任务性能提升超180%!
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
英伟达ViPE重磅发布!解决3D感知难题,SLAM+深度学习完美融合(附带数据集下载地址)
深度学习·算法·计算机视觉