深入理解滑动窗口算法及其经典应用

news2024/11/16 1:54:11

Kevin的技术博客.png

文章目录

  • 什么是滑动窗口?
  • 经典题型分析与讲解
      • **1. 长度最小的子数组**
      • **2. 无重复字符的最长子串**
      • **3. 最长重复子数组**
      • **4. 将x减到0的最小操作数**
      • 5. 水果成篮 (LeetCode 904)
      • 6. 滑动窗口最大值 (LeetCode 239)
      • 7. 字符串中的所有字母异位词 (LeetCode 剑指 Offer II 015)
      • 8. 串联所有单词的子串 (LeetCode 30)
      • 9. 最小覆盖子串 (LeetCode 76)
  • 总结

在算法设计中,滑动窗口是一种高效的技巧,尤其在处理连续子数组或子串问题时非常有用。滑动窗口的核心思想是使用两个指针,定义一个范围,在这个范围内计算所需的值,然后根据问题的要求移动窗口的边界。本文将详细剖析几道经典题目,结合代码来讲解滑动窗口的原理和应用。

什么是滑动窗口?

滑动窗口技术通常用于解决子数组或子串相关的问题。其主要思想是在数组或字符串上维持一个固定的窗口大小,或在特定条件下调整窗口大小,从而在窗口内进行高效的计算。滑动窗口技术可以帮助我们在O(n)的时间复杂度内解决一些需要遍历整个数组或字符串的问题。
滑动窗口的基本步骤包括:

  1. 初始化窗口的左右边界(通常为两个指针)。
  2. 移动窗口的右边界扩展窗口范围,直至满足某些条件。
  3. 移动窗口的左边界收缩窗口,直至不再满足条件。
  4. 记录或更新需要的结果。

接下来,我们通过几道经典的滑动窗口问题,来深入理解这一技巧的应用。

经典题型分析与讲解

1. 长度最小的子数组

题目描述:
给定一个含有n个正整数的数组和一个正整数
**target**,找出该数组中满足其和大于等于**target**的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回0。
滑动窗口思路:

  1. 我们使用两个指针**left****right**表示窗口的左右边界。
  2. 初始时两个指针都指向数组的起点。
  3. 扩展**right**指针,使窗口内的数字和逐渐增大。
  4. 当窗口内的和大于等于**target**时,收缩**left**指针以找到最小的子数组长度。
  5. 在整个过程中,动态更新最小长度。
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0;
        int right = 0;
        int len = INT_MAX;  // 初始化最小子数组长度为无穷大
        int n = nums.size();
        int sum = 0;  // 当前窗口的数字和

        // 遍历数组,扩展右边界
        for (; right < n; right++) {
            sum += nums[right];  // 将当前右边界的数字加入窗口的和中

            // 当窗口内的和大于或等于目标值时,缩小窗口
            while (sum >= target) {
                len = min(len, right - left + 1);  // 更新最小子数组长度
                sum -= nums[left];  // 减去左边界的数字,将窗口左边界右移
                left++;
            }
        }

        // 如果找不到符合条件的子数组,返回0;否则返回最小长度
        return len == INT_MAX ? 0 : len;
    }
};

复杂度分析:

  • 时间复杂度:O(n),其中n为数组的长度。每个元素在扩展和收缩窗口的过程中最多只会被访问两次。
  • 空间复杂度:O(1),仅使用了常数个额外空间。

2. 无重复字符的最长子串

题目描述:
给定一个字符串
**s**,请你找出其中不含有重复字符的最长子串的长度。
滑动窗口思路:

  1. 使用一个哈希表**hash**来记录窗口内字符的频率。
  2. 移动**right**指针扩展窗口,加入字符到哈希表中。
  3. 如果窗口内出现重复字符,则移动**left**指针收缩窗口,直到不再有重复字符。
  4. 在整个过程中,动态更新最大子串长度。
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[200] = { 0 };  // 用于记录字符的频率
        int left = 0;
        int right = 0;
        int ret = 0;

        while (right < s.size()) {
            hash[s[right]]++;

            // 当出现重复字符时,收缩窗口
            while (hash[s[right]] > 1) {
                hash[s[left++]]--;
            }

            // 更新最长子串长度
            ret = max(ret, right - left + 1);
            right++;
        }

        return ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n),字符串的每个字符最多访问两次。
  • 空间复杂度:O(1),哈希表的大小为固定的常数级。

3. 最长重复子数组

题目描述:
给定一个二进制数组
**nums**和一个整数**k**,如果可以将最多**k****0**变成**1**,求最长的连续**1**的长度。
滑动窗口思路:

  1. 使用两个指针**left****right**表示滑动窗口。
  2. 每次扩展**right**指针,将遇到的**0**记录在计数器**counter**中。
  3. 当窗口内**0**的个数大于**k**时,收缩窗口,直到**0**的个数不超过**k**
  4. 在整个过程中,动态更新最大连续**1**的长度。
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int left = 0;
        int right = 0;
        int ret = 0;
        int counter = 0;  // 记录窗口内0的个数

        for (; right < nums.size(); ++right) {
            if (nums[right] == 0) counter++;  // 遇到0则计数器加1

            // 当窗口内0的个数超过k时,收缩窗口
            while (counter > k) {
                if (nums[left] == 0) counter--;
                left++;
            }

            // 更新最大长度
            ret = max(ret, right - left + 1);
        }

        return ret;  // 返回最大长度
    }
};

复杂度分析:

  • 时间复杂度:O(n),数组的每个元素最多访问两次。
  • 空间复杂度:O(1),仅使用了常数个额外空间。

4. 将x减到0的最小操作数

题目描述:
给定一个整数数组
**nums**和一个整数**x**,你可以从数组的开头或者末尾取元素来减小**x**。请你返回最少需要多少次操作才能将**x**减少到**0**,如果无法实现,返回**-1**
滑动窗口思路:

  1. 计算数组总和**sum**,目标是找到一个和为**sum - x**的最长子数组。
  2. 使用滑动窗口来找这个最长的子数组。
  3. 如果找到了目标子数组,则返回最少操作数,即**nums.size() - 最大子数组长度**
  4. 否则返回**-1**
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum = accumulate(nums.begin(), nums.end(), 0);  // 计算数组总和
        int target = sum - x;  // 目标是找和为 sum - x 的子数组

        if (target < 0) return -1;  // 如果目标和为负数,直接返回-1
        if (target == 0) return nums.size();  // 如果目标和为0,返回整个数组长度

        int ret = 0, tmp = 0;

        for (int left = 0, right = 0; right < nums.size(); right++) {
            tmp += nums[right];

            // 当 tmp 大于目标值时,缩小窗口
            while (tmp > target) {
                tmp -= nums[left];
                left++;
            }

            // 找到一个和为 target 的子数组,更新 ret
            if (tmp == target) ret = max(ret, right - left + 1);
        }

        // 返回最少操作数
        return ret == 0 ? -1 : nums.size() - ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n),数组的每个元素最多访问两次。
  • 空间复杂度:O(1),仅使用了常数个额外空间。

5. 水果成篮 (LeetCode 904)

题目描述:
在一条树木组成的行上,有 n 棵树,每棵树上都挂着不同种类的水果。你只有两个篮子,每个篮子只能装一种类型的水果。你需要尽可能多地收集水果,但每次只能从连续的树上收集。
滑动窗口思路:
这道题可以看作是一个典型的滑动窗口问题,要求在一个数组中找到最多包含两个不同元素的最长子数组。我们通过滑动窗口来动态地调整当前子数组的左右边界,以找到满足条件的最长子数组。
代码分析:

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int, int> basket; // 用于记录每种水果的数量
        int max_fruits = 0; // 记录最多的水果数量
        int left = 0; // 窗口的左边界

        // 右边界从0开始移动
        for (int right = 0; right < fruits.size(); ++right) {
            basket[fruits[right]]++; // 将当前水果加入到篮子中

            // 如果篮子中的水果种类超过两种,调整左边界
            while (basket.size() > 2) {
                basket[fruits[left]]--; // 移除左边界的水果
                if (basket[fruits[left]] == 0) {
                    basket.erase(fruits[left]); // 种类数量为0时移除该种类
                }
                left++; // 移动左边界
            }

            // 更新最大水果数量
            max_fruits = max(max_fruits, right - left + 1);
        }

        return max_fruits; // 返回结果
    }
};

详细解读:

  1. 初始化与边界控制basket 是一个哈希表,用于记录当前窗口中每种水果的数量。left 是窗口的左边界,right 是窗口的右边界。
  2. 窗口扩展:通过 right 指针逐渐扩展窗口,将当前水果加入到篮子中。
  3. 窗口收缩:如果篮子中的水果种类超过两种,开始通过 left 指针收缩窗口,直到篮子中只有两种或更少种类的水果。
  4. 结果更新:每次调整窗口后,计算当前窗口的长度,并更新 max_fruits,以记录目前为止可以收集的最多水果数量。
  5. 返回结果:遍历整个数组后,max_fruits 中记录的就是最多的连续水果数量。

6. 滑动窗口最大值 (LeetCode 239)

题目描述:
给定一个数组 nums 和滑动窗口大小 k,请找出所有滑动窗口里的最大值。
滑动窗口 + 双端队列思路:
这道题的难点在于如何在每次滑动窗口移动时,快速找到当前窗口的最大值。我们可以借助一个双端队列 deque 来解决这个问题。deque 中保存的是元素在数组中的索引,并且这些索引对应的元素值在 deque 中是从大到小排列的。
代码分析:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;  // 存储数组元素的索引
        vector<int> result;

        for (int i = 0; i < nums.size(); i++) {
            // 移除不在窗口范围内的元素
            if (!dq.empty() && dq.front() < i - k + 1) {
                dq.pop_front();
            }

            // 移除队列中比当前元素小的元素
            while (!dq.empty() && nums[dq.back()] <= nums[i]) {
                dq.pop_back();
            }

            dq.push_back(i);  // 将当前元素的索引添加到队列

            // 当窗口大小达到 k 时,记录当前窗口的最大值
            if (i >= k - 1) {
                result.push_back(nums[dq.front()]);
            }
        }

        return result;
    }
};

详细解读:

  1. 初始化队列与结果数组deque 用于存储数组元素的索引,result 存储最终的最大值。
  2. 维护双端队列:在遍历 nums 时,首先检查 deque 的头部索引是否在当前窗口外,如果在则移除。然后,移除 deque 中所有比当前元素小的元素,因为这些元素不可能成为当前窗口的最大值。
  3. 记录结果:当窗口的大小达到 k 时,deque 的头部元素就是当前窗口的最大值,将其添加到 result 中。
  4. 返回结果:遍历完成后,返回 result,其中存储了每个滑动窗口的最大值。

7. 字符串中的所有字母异位词 (LeetCode 剑指 Offer II 015)

题目描述:
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
滑动窗口思路:
这道题与滑动窗口的使用密切相关,我们通过一个滑动窗口来逐步遍历字符串 s,同时维护一个与字符串 p 的字符频率相匹配的哈希表,以此来判断当前窗口是否为 p 的字母异位词。
代码分析:

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;  // 存储结果的向量
        int hash1[26] = { 0 };  // 存储字符串 p 中字符的频率

        // 计算字符串 p 中每个字符的频率
        for (auto ch : p)
            hash1[ch - 'a']++;

        int hash2[26] = { 0 };  // 存储当前滑动窗口中字符的频率
        int left = 0, count = 0;

        for (int right = 0; right < s.size(); right++) {
            char in = s[right];
            hash2[in - 'a']++;
            if (hash2[in - 'a'] <= hash1[in - 'a'])
                count++;

            // 当窗口长度超过字符串 p 的长度时,调整窗口
            if (right - left + 1 > p.size()) {
                char out = s[left];
                if (hash2[out - 'a']-- <= hash1[out - 'a'])
                    count--;
                left++;
            }

            // 如果 count 等于 p 的长度,说明找到一个字母异位词
            if (count == p.size())
                ret.push_back(left);
        }

        return ret;
    }
};

详细解读:

  1. 哈希表初始化hash1 存储字符串 p 中每个字符的频率。
  2. 窗口扩展right 指针逐步扩展窗口,将当前字符添加到 hash2 中,并检查是否符合 p 的字符频率。
  3. 窗口收缩:当窗口大小超过 p 的长度时,调整 left 指针,移除最左边的字符,并更新 hash2 中的频率。
  4. 结果记录:如果当前窗口中符合 p 的所有字符频率,则记录当前窗口的起始位置。
  5. 返回结果:最终返回 ret,其中存储了所有符合条件的起始索引。

8. 串联所有单词的子串 (LeetCode 30)

题目描述:
给定一个字符串 s 和一个字符串数组 words,找出 s 中所有可以由 words 中所有单词串联形成的子串的起始位置。
滑动窗口思路:
这道题可以看作是将每个单词视为一个单位的滑动窗口问题,我们需要找到一个窗口,使得其中包含 words 中的所有单词,并且每个单词出现的次数都与 words 中的频率一致。
代码分析:

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> ret;
        unordered_map<string, int> hash1;  // 保存 words 里面所有单词的频次

        // 统计 words 中每个单词的出现次数
        for (auto& word : words)
            hash1[word]++;

        int len = words[0].size();  // 单词的长度
        int m = words.size();  // 单词的数量

        // 执行 len 次(从不同的起点开始)
        for (int i = 0; i < len; i++) {
            unordered_map<string, int> hash2;  // 维护窗口内单词的频次
            for (int left = i, right = i, count = 0; right + len <= s.size(); right += len) {
                // 进窗口并维护 count
                string in = s.substr(right, len);
                hash2[in]++;
                if (hash2[in] <= hash1[in])  count++;

                // 判断窗口大小是否超过了允许的大小,进行出窗口操作并维护 count
                if (right - left + len > len * m) {
                    string out = s.substr(left, len);
                    if (hash2[out] <= hash1[out]) count--;
                    hash2[out]--;
                    left += len;
                }

                // 更新结果,如果当前窗口内的单词数量和 words 中一致,记录左边界
                if (count == m)  ret.push_back(left);
            }
        }

        return ret;
    }
};

详细解读:

  1. 哈希表初始化hash1 用于存储 words 中每个单词的频率。
  2. 窗口扩展right 指针逐步扩展窗口,将当前单词添加到 hash2 中,并检查是否符合 words 中的频率。
  3. 窗口收缩:如果当前窗口大小超过了 words 中所有单词串联后的长度,则调整 left 指针,移除最左边的单词,并更新 hash2
  4. 结果记录:当 count 等于 words 的长度时,说明当前窗口符合要求,将窗口的起始位置 left 记录到 ret 中。
  5. 返回结果:最终返回 ret,其中存储了所有符合条件的起始索引。

9. 最小覆盖子串 (LeetCode 76)

题目描述:
给定一个字符串 s 和一个字符串 t,找到 s 中包含 t 的所有字符的最小子串。
滑动窗口思路:
我们需要维护一个滑动窗口,使得窗口中的子串包含 t 的所有字符,且窗口尽可能小。
代码分析:

class Solution {
public:
    string minWindow(string s, string t)
    {
        int hash1[128] = { 0 };
        int kinds = 0;

        // 初始化hash1,计算需要匹配的字符种类数kinds
        for (auto a : t)
        {
            if (hash1[a] == 0) kinds++;
            hash1[a]++;
        }

        int hash2[128] = { 0 };
        int begin = -1;  // 用于记录最小子串的起始位置:
                int begin = -1;  // 用于记录最小子串的起始位置
        int min_len = INT_MAX;  // 记录最小子串长度

        for (int left = 0, right = 0, count = 0; right < s.size(); ++right)
        {
            char in = s[right];
            hash2[in]++;

            // 如果当前字符频率与目标频率匹配,增加count
            if (hash2[in] == hash1[in]) count++;

            // 当所有字符频率都匹配时,开始尝试缩小窗口
            while (count == kinds)
            {
                // 更新最小子串的起始位置和长度
                if (right - left + 1 < min_len)
                {
                    min_len = right - left + 1;
                    begin = left;
                }

                char out = s[left];
                left++;

                // 移出左边界字符后,判断是否需要减少count
                if (hash2[out] == hash1[out]) count--;
                hash2[out]--;
            }
        }

        // 如果没有找到满足条件的子串,返回空字符串
        if (begin == -1) return "";
        return s.substr(begin, min_len);
    }
};

详细解读:

  1. 初始化哈希表hash1 用于记录字符串 t 中每个字符的频率,并计算需要匹配的字符种类数 kindshash2 用于记录当前窗口中字符的频率。
  2. 窗口扩展right 指针逐步扩展窗口,将当前字符添加到 hash2 中。如果当前字符在 hash2 中的频率与 hash1 中的频率相同,则增加 count
  3. 窗口收缩:当 count 等于 kinds 时,意味着当前窗口已经包含了 t 中的所有字符,此时尝试缩小窗口。如果缩小后的窗口仍然包含 t 中的所有字符,则更新最小子串的起始位置和长度。
  4. 判断结果:如果最终找到了符合条件的子串,返回该子串,否则返回空字符串。

总结

上述算法都使用了滑动窗口技术来解决问题。滑动窗口的核心思想是逐步扩展窗口,同时保持窗口的最优状态,尽可能减少不必要的计算。通过维护一个哈希表来记录窗口内的字符频率或单词频率,可以有效地判断当前窗口是否满足题目要求。每当窗口状态符合要求时,记录当前的结果,并尝试收缩窗口以找到更优解。

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP S4HANA 2023 FPS01 FAA虚拟机发布了

SAP S4HANA 2023 FPS01 FAA虚拟机发布了。 系统不再需要修改虚拟机日期了&#xff0c;提供最高长达三年的许可&#xff0c;业务财务做账都是真实的时间&#xff01; 该虚拟机版本优点&#xff1a; 新版的一键启动脚本&#xff0c;3分钟就能启动完成。 内存加载 80GB 就可以启动…

二叉树检验:算法详解

问题描述 /** 检查二叉树是否为有效的二叉搜索树有效的二叉搜索树满足左子树的节点值都小于根节点值&#xff0c;右子树的节点值都大于根节点值并且左右子树也必须是有效的二叉搜索树param root 二叉树的根节点return 如果二叉树是有效的二叉搜索树&#xff0c;则返回true&…

当AI成为你的私人医生,与AI“医”路同行的奇妙体验

“ 从挂号到诊疗&#xff0c;再到后续的健康管理&#xff0c;人工智能&#xff08;AI&#xff09;正以一种全新的方式融入我们的生活。上海市第一人民医院的创新实践&#xff0c;便是这一变革的生动注脚。 ” AI就医助理&#xff1a;从“助手”到“伙伴” 当你踏入医院大门…

01-容器基础:从进程说起

本章内容包括&#xff1a; 容器是什么样的一种技术容器的边界是怎么实现的容器支持哪些Namespace容器的本质是什么虚拟机与容器 在开始本章之前&#xff0c;我希望你能理解这样一个道理&#xff1a;容器本身没有价值&#xff0c;有价值的是"容器编排"。 那么容器究竟…

SD-WAN降低网络运维难度的关键技术解析

为什么说SD-WAN&#xff08;软件定义广域网&#xff09;大大降低了网络运维的复杂性&#xff0c;主要是因为它的智能路径选择、应用识别和链路质量监测这三个核心技术。这几项在SD-WAN中尤为重要的技术&#xff0c;它们共同作用&#xff0c;提升了整体网络性能&#xff0c;为网…

软件测试——自动化测试博客系统

代码gitee仓库地址——SoftwareTest 测试思路 注意用例之间的依赖 通过clear保证输入框内没有信息通过刷新保证输入框内没有信息不要在中间释放driver 登录成功 输入正确的账户密码后会跳转页面&#xff0c;可以检测是否能抓取到跳转页面上的与登录页面不同的某个元素通过…

【MySQL】黑马 MySQL基础 笔记

文章目录 概述数据模型关系型数据库 SQL通用语法分类DDL数据库操作表操作-查询表操作-创建表操作-数据类型表操作-修改 DML添加数据修改数据删除数据 DQL基本查询&#xff08;SELECT、FROM&#xff09;条件查询&#xff08;WHERE&#xff09;聚合函数(count、max、min、avg、su…

秒懂C++之红黑树

目录 前言 一. 红黑树的概念 二. 红黑树的性质 三. 红黑树的插入 插入代码 四. 红黑树的验证 InOrder IsBalance 五. 全部代码 前言 红黑树中涉及到了AVL树旋转的特点&#xff0c;如果对旋转不太了解可以去这篇文章&#xff1a;秒懂C之AVL树-CSDN博客 一. 红黑树的概…

ZNS SSD是不是持久缓存的理想选择?

随着数据量的增加和技术的进步&#xff0c;对于高效、可靠的存储解决方案的需求日益增长。传统的基于块的SSD虽然具有成本效益和持久性的优点&#xff0c;但在处理写密集型和更新密集型工作负载时存在局限性。 NAND闪存的特点是数据只能按页&#xff08;例如4KiB&#xff09;写…

【国赛】【美赛】【五一杯】【电工杯】【华数杯】【亚太杯】······各赛事历年优秀论文+真题分享

今天继续给大家分享十分重磅的资料哦&#xff0c;数学建模各大竞赛的资料汇总&#xff0c;可能很多小伙伴平时进行某个比赛的资料搜索的时候会发现&#xff0c;我们想要的这个比赛的资料有时候非常难搜索到&#xff0c;搜索23年&#xff0c;显示21年的&#xff0c;搜索小美赛&a…

Prometheus 监控指标采集

原文链接&#xff1a;https://www.hezebin.com/article/66b3b1fb4379b36dec11a1a1 前言 在现代分布式系统和云原生环境中&#xff0c;为了确保复杂的分布式系统和服务的高可用性、可靠性和性能&#xff0c;通常采用实时可视化监控和分析&#xff0c;实现故障快速响应、资源优…

单片机驱动彩屏最简方案:单片机_RA8889最小开发板驱动控制TFT彩屏介绍(二)硬件电路设计

本文介绍使用单片机RA8889来驱动和控制彩屏的最小方案。文章从RA8889的架构功能、硬件电路设计及软件设计三个方面来说明。 小编已发布多篇文章介绍了单片机RA8889来驱动控制彩屏&#xff0c;但是仍有不少单片机玩家可能对驱动彩屏还不算熟悉&#xff0c;在此加推一个短篇介绍…

机器学习-随机森林(全网最详解)

文章目录 一、简介1.定义2.基本原理3.优缺点4.应用场景 二、代码运用1.数据预处理2.模型训练3.模型评估4.绘制特征排名 三、总结 一、简介 1.定义 随机森林&#xff08;Random Forest&#xff09;是一种集成学习方法&#xff0c;它通过构建多个决策树并将它们的预测结果进行汇…

公司电脑被监控有什么表现?电脑监控VS员工隐私,员工合理摸鱼需知!职场小贴士为您解答!

公司电脑被监控有什么表现&#xff1f; 数字化办公日益普及&#xff0c;许多企业为了保障信息安全、提升工作效率&#xff0c;会选择在公司电脑上安装监控软件。这一举措在提升企业管理效能的同时&#xff0c;也引发了关于员工隐私与合理工作界限的讨论。本文将为您解析公司电…

k8s教程

1. k8s框架 - kubernetes的架构- Control Plane: 控制K8S集群的组件。- Api Server: 集群的访问入口。- etcd: 存储集群的数据。一般情况下&#xff0c;只有API-SERVER会访问.- Control Manager: 维护集群的状态。- Scheduler: 负责Pod的调度功能。- Wor…

防止拷贝电脑资料?【三种数据拷贝的详细方法分享!】

防止电脑资料被拷贝通常是指采取措施来保护敏感或专有信息不被未经授权的用户复制或传播。 这里我理解您可能想要了解的是如何防止数据被拷贝的方法&#xff0c;而不是进行数据拷贝的方法。 下面是一些常见的防止数据拷贝的策略和技术&#xff1a; 1. 物理安全控制 锁屏或密…

一篇讲完自动化基础-Python【万字详细讲解】

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 这篇文章主要学习Python的语法&#xff0c;为后续的自动化打基础 Python requests 接口自动化 Python selenium web 自动化 Python appium移动端自动化(手机 app) 这篇文章分六个阶段百分比进行划分&#xff0c;到时…

Elasticsearch:使用 semantic_text 进行语义搜索

警告&#xff1a;截止 8.15 版本&#xff0c;此功能处于测试阶段&#xff0c;可能会发生变化。设计和代码不如官方 GA 功能成熟&#xff0c;并且按原样提供&#xff0c;不提供任何保证。测试版功能不受官方 GA 功能的支持 SLA 约束。 本教程向你展示如何使用 semantic text 功能…

硬件检测工具箱 | 入梦工具箱 v8.8

入梦工具箱&#xff08;RM Toolbox&#xff09;是一款专为硬件检测、评分和测试设计的免费开源软件。它以其小巧的体积和简洁的界面&#xff0c;迅速成为DIY玩家和硬件爱好者的首选工具。 功能特点 集成常用硬件检测工具&#xff1a;包括CPUZ、GPUZ、AIDA64等&#xff0c;全面…