算法----BF算法KMP算法

请想象一个情景:

当你脑海中突然浮现出一个词,你该怎么去找到这个词的有关内容?

打开我们浏览器的搜索框,输入你想的这个词,然后点击Enter。浏览器就会自动搜索与该词匹配的内容。

这个过程实际上可以简化成以下形式:

有一个文本串S,一个模式串P(也叫子串),现在要查找PS中的位置。

我们今天所讨论的两个算法就是有关该过程的算法。

事实上,对于检索,无非就是两个字符串的匹配过程,模式串是你想要匹配的串,主串是你搜索所在串。

针对模式串中的一个个字符与主串进行匹配,

匹配成功则继续往后匹配;

匹配失败则跳过该串段继续匹配,直到主串中出现与模式串完全相同的串段,此时则成功找到。

所以检索无非就是模式匹配的过程。

BF算法和KMP算法是较为著名的模式匹配算法,接下来作出详细介绍。

BF算法

BF算法(Brute-Force)也称为暴力算法,其核心原理是逐个比较文本串和模式串的字符,如果匹配失败,则通过向右移动模式串的位置,再次进行比较。

算法步骤

我们设主串和模式串中的字符位置分别为ij

  • 如果当前字符匹配成功(即T[i] == P[j]),则i++,j++,继续匹配下一个字符;
  • 如果当前字符匹配失配(即T[i]! = P[j]),则令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0

举例:

假设我们有一个文本串T为:“ABCDABCDABCE”,以及一个模式串P为:“ABCE”,我们要在文本串T中查找是否存在模式串P。

首先,我们将文本串T和模式串P在一条直线上对齐:

文本串T:  ABCDABCDABCE
模式串P:  ABCE

然后,我们从文本串T的第一个字符开始和模式串P的第一个字符比较:

第一次比较:'A’和’A’相等。

文本串T:  ABCDABCDABCE
模式串P:  A

第二次比较:'B’和’B’相等。

文本串T:  ABCDABCDABCE
模式串P:  AB

第三次比较:'C’和’C’相等。

文本串T:  ABCDABCDABCE
模式串P:  ABC

第四次比较:'D’和’E’不相等,出现失配。

文本串T:  ABCDABCDABCE
模式串P:  ABCE

在匹配失败后,我们将模式串P向右移动一位,重新从文本串T的当前位置和模式串P的第一个字符开始比较:

文本串T:  ABCDABCDABCE
模式串P:   A

第一次比较:'A’和’B’不相等。出现失配。

那么再将模式串P向右移动一位,重新从文本串T的当前位置和模式串P的第一个字符开始比较:

文本串T:  ABCDABCDABCE
模式串P:    A

第一次比较:'A’和’C’不相等。出现失配。

继续类似的比较过程,直到文本串T遍历完毕。

代码演示

根据上述过程我们可以写出BF算法的代码:

int ViolentMatch(char* s, char* p)
{int sLen = strlen(t);int pLen = strlen(p);int i = 0;int j = 0;while (i < sLen && j < pLen){if (t[i] == p[j]){//①如果当前字符匹配成功(即T[i] == P[j]),则i++,j++    i++;j++;}else{//②如果失配(即T[i]! = P[j]),令i = i - (j - 1),j = 0    i = i - j + 1;j = 0;}}//匹配成功,返回模式串p在文本串s中的位置,否则返回-1if (j == pLen)return i - j;elsereturn -1;
}

我们发现这种确实可以被称作暴力解法:无论后面的元素是否匹配,模式串P都会回溯到它的第一个字符开始重新比较,而例如在第二次重新匹配的过程中,实际上是必定失配的,从而又要继续回溯再重新比较,属实暴力且死板。

时间复杂度

BF算法的时间复杂度取决于文本串T的长度为n,模式串P的长度为m。在最坏情况下,BF算法需要在文本串T的每个位置上都尝试匹配模式串P,因此时间复杂度为O(n*m)

在实际情况下,BF算法的效率并不高,特别是当文本串T和模式串P的长度很大时。对于较长的文本串和模式串,BF算法的时间复杂度可能会导致性能问题。

那么有没有另外一种解法,可以避免不必要的i回溯,而只移动j即可呢?这样所需要消耗的时间就会大大减少。

答案就是KMP算法。

KMP算法

KMP算法的核心思想是利用模式串自身的特点来加速匹配过程,避免重复匹配。

算法步骤

我们设主串和模式串中的字符位置分别为ij

  • 如果当前字符匹配成功(即T[i] == P[j]),步骤与暴力匹配法相同,则i++,j++,继续匹配下一个字符;
  • 如果当前字符匹配失配(即T[i]! = P[j]),则根据最大长度表计算需要移动的位数。

这里由于匹配成功的情况与前面BF相同,所以我们只对匹配失败进行讨论。

公式

需要移动的位数=已匹配的字符数-失配字符的上一位字符对应的最大长度

这里我们理解一下具体的步骤,以及为什么是上述的公式。

为什么是算出最大长度的相同前缀和后缀

因为当最大长度的前缀和后缀相同的时候,移动已匹配的字符数-最大长度即可保证不会多移动或者少移动。如图:

在这里插入图片描述

蓝色部分的字符个数即是我们需要移动的字符个数,而黄色部分的字符个数即是最大相同长度,而黄色+蓝色部分即是

已匹配的字符个数

所以有:已匹配的字符个数=最大相同长度+需要移动的字符个数,从而得出:

需要移动的字符个数=已匹配的字符个数-最大相同长度

注意:我们要找的是相同前后缀的最长长度,注意一定是要最长的。并且不能是字符串本身。(如果是本身便没有意义了)

最大长度表是什么

上述过程我们所提到的最大长度表指的是模式串中最大长度的相同前缀和后缀,在公式中的最大相同长度就是对照该表得出的。

在这里插入图片描述

如何求最大长度表呢?在具体的代码中,我们需要使用一个函数来求出最大长度表,并且在具体的算法实现中,让其对应求出所需要移动的字符个数。这个时候我们就需要用到next数组


next数组

首先我们最需要知道的是:next数组的作用就是求出最大长度表。

最大长度表并不是使用next数组求出来的对照表,而是指的是next数组本身。next数组存储的是最大长度表,用于帮助算法快速定位匹配位置;

而由于数组的初始下标为0的限制,在书写上两者会有以下的差异:

next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。(因为数组的初始下标为0)

也就是 j-next[j]

在这里插入图片描述

问题的关键就是寻找模式串中最大长度的相同前缀和后缀,找到了模式串中每个字符之前的前缀和后缀公共部分的最大长度后,便可基于此匹配。而这个最大长度便正是next 数组要表达的含义。

next的具体求法

针对主串和模式串来进行字符匹配,而p[k]是主串正在被匹配的子字符串的元素,p[j]是正在进行匹配的模式串的元素。

  • 如果模式串的第i个字符和第j个字符相等(即p[k ] == p[j]),则next[j + 1] = next[j] + 1 = k + 1,同时i和j都向后移动一位。

也就是说此时:t(1)t(2)…t(k)=t(j-k+1)t(j-k+2)…t(j)

那么next[j + 1] = next[j] + 1 = k + 1。代表此字符前的模式串中,有长度k+1 的相同前缀后缀。

  • 如果模式串的第i个字符和第j个字符不相等(即p[k ] ≠ p[j]),则next[ j + 1 ] = next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 也就是说此时正在匹配的字符失配,而算出的next[ j + 1 ] = next[k] + 1就是最大长度。

注意:主串是永远不动的,动的一直都是子串也就是模式串,也就是说i永不递减,只有j会递减。

next数组的代码使用递推求解,好处在于会不断利用已掌握的信息来避免重复的运算。

针对上述的字符不相同的情况,我们对此进行更详细的解答。

首先我们应该以一种类动态规划的思想去思考这个问题:动态规划中,我们会利用已知的子问题来解决更大规模的问题,避免重复的计算;而在KMP中,next数组存储了模式串中的最大长度,这个最大长度会帮助我们跳过一些不必要的比较,这个在后面会提到。

接下来,我们进行模拟。

当匹配不成功时:

查找是否存在更短的共同前后缀,如果找到了,则重新从此处再做一次KMP算法

例如,有以下字符串:

A  B  A  C  A  B  A  B
0  1  2  3  4  5  6  7

扫描到 6 号位的 A 时,最长公共前后缀是 ABA;而扫描到 7 号位的 B 时,ABAC 和 ABAB 不匹配了,即原来的最长公共前后缀失配。

这时候我们要做的事情就是,找上一次匹配中次长的公共前后缀,看与 7 号位的 B 拼接起来是否能匹配。因为我们的目的就是为了继续匹配,但是由于最长的已经用过了,所以就找次长的。

这时候,注意到上一次扫描中 0 ~ 2 位的 ABA 是和 4 ~ 6 位的 ABA 完全相同的,所以考察上一次匹配中次长的公共前后缀,只能在考察上一次匹配中的最长公共前后缀中寻找,也就是说,只能考察 ABA 中更短的 BA、A 是否是次长的,而这直接在前面一个 ABA 中考察就行(因为前后两个ABA是一样的)。(我们的目的就是要找最长公共前后缀)

所以我们把 ABA C ABA 的中间部分(C)和后缀(ABA)直接抛弃,等效于一个串 ABA(也就是前缀)与 B 拼接成 ABAB。这样再来计算第 7 位的 B 的 next 值,等价于计算 ABAB 第 3 位的 B 的 next 值。

代码演示
void GetNext(char* p,int next[])
{int pLen = strlen(p);next[0] = -1;int k = -1;int j = 0;while (j < pLen - 1){//p[k]表示前缀,p[j]表示后缀if (k == -1 || p[j] == p[k]) {++k;++j;next[j] = k;}else {k = next[k];}}
}

这样我们就能计算出我们需要的next值,从而得到最大长度表,然后再将该表代入到需要移动字符数的计算公式中,继续匹配下去。

当然

接下来根据KMP算法的主要核心以及next数组举例介绍流程:

假设我们有文本串 T = "ABCABABCABD" 和模式串 P = "ABCABD"。我们需要在文本串 T 中找到模式串 P 的出现位置。

步骤1: 构建next数组

首先,我们需要为模式串 P 构建一个next数组。这个数组用于在不匹配的情况下,告诉我们应该从模式串的哪个位置重新开始匹配。

对于模式串 P = "ABCABD",我们如下构建next数组:

  1. 初始化:next[0] = -1 (当模式串的第一个字符不匹配时,我们没有更早的位置可以回退到)
  2. P[1] 开始,比较前后缀:
    • j = 0next[1] = 0 (没有相同的前后缀)
    • j = 1,比较 P[0]P[1],不相同,next[2] = 0
    • j = 2,比较 P[0]P[2],不相同,next[3] = 0
    • j = 3,比较 P[1]P[3],相同,next[4] = 1
    • j = 4,比较 P[2]P[4],相同,next[5] = 2
    • j = 5,比较 P[3]P[5],不相同,next[6] = 0

最终的next数组为 [-1, 0, 0, 0, 1, 2, 0].

步骤2: 使用next数组进行匹配

现在我们使用next数组来匹配文本串 T 和模式串 P

T = ABCABABCABD
P = ABCABD
  1. 初始位置:i = 0, j = 0
    • T[0] = P[0] (A = A),匹配,i = 1, j = 1
    • T[1] = P[1] (B = B),匹配,i = 2, j = 2
    • T[2] = P[2] (C = C),匹配,i = 3, j = 3
    • T[3] = P[3] (A = A),匹配,i = 4, j = 4
    • T[4] = P[4] (B = B),匹配,i = 5, j = 5
    • T[5] = P[5] (C ≠ D),不匹配,根据 next[5] = 2j = 2
  2. 继续匹配:
    • T[5] = P[2] (C = C),匹配,i = 6, j = 3
    • T[6] = P[3] (A = A),匹配,i = 7, j = 4
    • T[7] = P[4] (B = B),匹配,i = 8, j = 5
    • T[8] = P[5] (D = D),匹配,i = 9, j = 6(模式串匹配完成)

匹配成功,模式串 P 在文本串 T 中的起始位置为 3 (从0开始计数)。

通过这个例子,我们可以看到KMP算法如何有效地使用next数组来避免不必要的比较,从而加快字符串匹配的过程。

nextval(next的扩展优化)

请看以下举例

T = AAABAAAAB
P = AAAAB

当i=4,j=4时,我们发现此时的字符不匹配,那么根据next[j]的指示我们还需进行:i=4不变,分别j=3、j=2、j=1的三次比较。

但是实际上模式串中的13以及第4个字符全都相等,因此将13这3个字符再去和主串的第4个字符比较实际上是和j=4时的情况是一样的,是会失配的。

在这里插入图片描述

那么我们应该如何避免并跳过这种重复的比较,直接进行i=5、j=1的比较呢?

这里则可以对next进行优化得到nextval

//优化过后的next 数组求法
void GetNextval(char* p, int next[])
{int pLen = strlen(p);next[0] = -1;int k = -1;int j = 0;while (j < pLen - 1){//p[k]表示前缀,p[j]表示后缀  if (k == -1 || p[j] == p[k]){++j;++k;//较之前next数组求法,改动在下面4行if (p[j] != p[k])next[j] = k;   //之前只有这一行else//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]next[j] = next[k];}else{k = next[k];}}
}

这个代码就完美地规避了上述讲到的问题。nextval可以实现跳跃到一个更远的位置进行匹配,从而减少不必要的比较次数。


接下来的代码是整个KMP算法的代码

代码演示

int KmpSearch(char* s, char* p)
{int i = 0;int j = 0;int sLen = strlen(s);int pLen = strlen(p);while (i < sLen && j < pLen){//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++    if (j == -1 || s[i] == p[j]){i++;j++;}else{//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]    //next[j]即为j所对应的next值      j = next[j];}}if (j == pLen)return i - j;elsereturn -1;
}

时间复杂度

KMP算法的时间复杂度分析如下:

  1. 构建next数组的时间复杂度:构建next数组的时间复杂度是O(m),其中m是模式串的长度。

  2. 匹配过程的时间复杂度:在KMP算法中,匹配过程的时间复杂度主要取决于文本串的长度n。在匹配过程中,每次失配时,根据next数组回退到一个更早的位置重新开始匹配,而不会重复比较已经匹配过的字符。因此,匹配过程的时间复杂度是O(n)。

综合以上两点,KMP算法的总体时间复杂度为O(m + n),其中m是模式串的长度,n是文本串的长度。相比于朴素的字符串匹配算法的**O(m*n)**时间复杂度,KMP算法通过利用next数组的特性,在匹配过程中避免了不必要的比较,从而实现了更高效的字符串匹配。

KMP的使用场景

总的来说,KMP算法适用于需要快速匹配模式串的场景,特别是在文本串较长、模式串较短的情况下。为什么呢?我们参照KMP的时间复杂度,O(m + n)m是模式串的长度,n是文本串的长度:

  • 长文本串(针对n):KMP算法适用于处理长文本串,因为它能够在匹配过程中避免不必要的比较,从而减少比较次数,提高匹配效率。
  • 短模式串(针对m):KMP算法在处理短模式串时效果显著,因为其时间复杂度不会随着模式串长度的增加而大幅增加。
  • 需要多次匹配:如果需要在同一文本串中多次匹配同一个模式串,KMP算法可以提高效率,因为构建好的next数组可以被重复利用。

常用用途

  • 字符串搜索:KMP算法常用于在文本串中搜索特定的模式串,例如搜索关键字、词语等。

  • 文本处理:在文本处理领域,KMP算法可以用于文本匹配、替换等操作。

  • 编译器设计:在编译器的词法分析阶段,KMP算法用于匹配词法单元,如关键字、标识符等。

  • 网络协议:在网络协议中,KMP算法可以用于匹配特定的模式,例如在URL匹配、数据包匹配等场景中。
    *是文本串的长度:

  • 长文本串(针对n):KMP算法适用于处理长文本串,因为它能够在匹配过程中避免不必要的比较,从而减少比较次数,提高匹配效率。

  • 短模式串(针对m):KMP算法在处理短模式串时效果显著,因为其时间复杂度不会随着模式串长度的增加而大幅增加。

  • 需要多次匹配:如果需要在同一文本串中多次匹配同一个模式串,KMP算法可以提高效率,因为构建好的next数组可以被重复利用。

常用用途

  • 字符串搜索:KMP算法常用于在文本串中搜索特定的模式串,例如搜索关键字、词语等。
  • 文本处理:在文本处理领域,KMP算法可以用于文本匹配、替换等操作。
  • 编译器设计:在编译器的词法分析阶段,KMP算法用于匹配词法单元,如关键字、标识符等。
  • 网络协议:在网络协议中,KMP算法可以用于匹配特定的模式,例如在URL匹配、数据包匹配等场景中。

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

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

相关文章

配置有效的防爬虫技术保护网站

本文主要介绍了防爬虫的概念、目的以及一些有效的防爬虫手段。防爬虫是指网站采取各种技术手段阻止爬虫程序对其数据进行抓取的过程。为了保护网站的数据和内容的安全性&#xff0c;防止经济损失和恶意竞争&#xff0c;以及减轻服务器负载&#xff0c;网站需要采取防爬虫机制。…

c++中的指针

一、指针的基本概念 指针的作用&#xff1a;可以通过指针间接访问内存 内存编号是从0开始记录的&#xff0c;一般采用16进制数字表示。可以利用指针变量保存地址。 二、指针变量的定义和使用 指针变量定义语法&#xff1a; 数据类型 * 变量名 #include<iostream> u…

多组学+机器学习+膀胱癌+分型+建模

这是一个基于多组学机器学习的分型建模文章&#xff0c;这里我们大概介绍一下&#xff0c;这篇文章做了啥 一、研究背景 1、尿路上皮癌是高度恶性的肿瘤&#xff0c;预后差&#xff0c;死亡率高 2、没有明显有效的治疗方法&#xff0c;多数患者在免疫治疗中无法受益&#xf…

【基于YOLOv8的教室人脸识别 附源码 数据集】

基于YOLOv8的教室人脸识别 附源码 数据集 在当今数字化迅速发展的教育领域中&#xff0c;人脸识别技术已成为提高校园安全和教学效率的关键工具。本文将详细介绍基于最新YOLOv8算法的教室人脸识别系统&#xff0c;这一系统不仅能够实时准确地识别学生和教职工的面部特征&#…

1小时学会SpringBoot3+Vue3前后端分离开发

首发于Enaium的个人博客 引言 大家可能刚学会Java和Vue之后都会想下一步是什么&#xff1f;那么就先把SpringBoot和Vue结合起来&#xff0c;做一个前后端分离的项目吧。 准备工作 首先你需要懂得Java和Vue的基础知识&#xff0c;环境这里就不多说了&#xff0c;直接开始。 …

switch语句深讲

一。功能 1.选择&#xff0c;由case N:完成 2.switch语句本身没有分支功能&#xff0c;分支功能由break完成 二。注意 1.switch语句如果不加break&#xff0c;在一次判断成功后会执行下面全部语句并跳过判断 2.switch的参数必须是整形或者是计算结果为整形的表达式,浮点数会…

科普童话新课堂杂志社科普童话新课堂编辑部2024年第16期目录

作品选 封2,封3-封4 探索新知《科普童话》投稿&#xff1a;cn7kantougao163.com 泱泱国之风 悠悠诗之情 沈灿宇1-3 试论"文化意识"视角下的高中英语阅读教学 董娜4-6 立足小组合作探究优化写作能力培养 时同祥7-9 以"导"促学:全面提升学生的文学核心素养 吴…

平抑风电波动的电-氢混合储能容量优化配置

这篇论文中的EMD分解法在非线性扰动信号分解上优于小波分解法,EMD分解出来的imf各频次信号,继而利用C2F实现信号重构,根据最大波动量限值剔除出需要储能平抑的波动量,继而用超级电容实现平抑(论文中用的碱水电解槽+燃料电池我认为有很多个点可以佐证不合适,但是电制氢是热…

Redis 的持久化机制有哪些??

一、背景&#xff1a; 持久化&#xff0c;就是将数据存入到磁盘中去的过程。虽然redis是基于内存运行的服务&#xff0c;但是也需要做持久化操作的。 二、redis服务等持久化机制流程&#xff1a; Redis是基于内存的非关系型K-V数据库&#xff0c;既然它是基于内存的&#xff0c…

c# winform 控件皮肤

控件皮肤下载&#xff1a; https://download.csdn.net/download/m0_46973223/89225992 步骤&#xff1a; 第一步 将IrisSkin4.dll文件放在debug文件下&#xff0c;选一个或者多个后缀名为.ssk文件&#xff08;各个皮肤文件&#xff09;放在debug文件下。 第二步 解决方案资…

全额退款20000,what?

接单的时候有多兴奋&#xff0c;退单的时候就有多落寞。今天我对客户全额退款了&#xff0c;跟踪了10天的项目正式结束。 这是我接单以来项目单价最高的一个项目&#xff0c;本来不太想接的&#xff0c;因为业务领域不擅长&#xff0c;又想挑战一下。兜兜转转找了几个人因为各种…

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…

机器学习——过拟合

一、过拟合得表现 模型在训练过程中&#xff0c;除了会出现过拟合现象&#xff0c;还有可能出现欠拟合的情况。相比而言&#xff0c;后者通常发生在建模前期&#xff0c;只要做好特征工程一般可以解决模型欠拟合问题。下图描述了模型在训练数据集上的三种情况&#xff1a; 其…

【LeetCode:2095. 删除链表的中间节点 + 链表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

BGP的路径属性

路径属性 l每条BGP路由都拥有多个的路径属性&#xff0c;有些是必须携带的&#xff0c;有些是可选添加的 lBGP的路径属性将影响最优路由的选择 lBGP路径属性是描述路由的一组参数&#xff0c;BGP根据路由的属性选择最佳路由&#xff0c;可以人为置值&#xff0c;以便执行路由…

[Android]引导页

使用Kotlin Jetpack Compose创建一个左右滑动的引导页, 效果如图. 1.添加依赖项 androidx.compose.ui最新版本查询:https://maven.google.com/web/index.html com.google.accompanist:accompanist-pager最新版本查询:https://central.sonatype.com/ 确保在 build.gradle (M…

算法-KMP算法

时间复杂度&#xff1a; public int strStr(String haystack, String needle) {int[] next new int[needle.length()];//next数组的生成next[0] 0;int prefixLen 0;//共同前后缀长度int i 1, j 1;//i,j复用while (i < needle.length()) {if (needle.charAt(prefixLen)…

数据库工程师的工作职责(合集)

数据库工程师的工作职责1 职责&#xff1a; 1. 日常数据库的基本安装&#xff0c;维护&#xff0c;升级&#xff0c;监控的; 2. 配合研发部门进行数据库设计支持&#xff0c;协助开发、设计和进行SQL语言优化; 3. 配合相关部门数据库相关的任务&#xff0c;比如数据导入导出&am…

「屡教不改」又被罚,谁还在相信山姆?

值得信赖的品质」「全球进口品牌」……「更好的生活尽在山姆」。 作为全球最大的会员制商店&#xff0c;山姆这几年可以说在中国线下零售市场集体遇劫的背景下&#xff0c;逆势崛起&#xff0c;甚至可以说做到了独树一帜。‍‍‍‍‍‍ 背后的原因是什么&#xff1f;会员制这一…

迅雷不限速破解方法

背景&#xff1a;现在迅雷和百度云的下载速度真的太恶心了&#xff0c;所以总有大佬可以采用厉害的方法进行破解&#xff0c;在网上看了一圈&#xff0c;很多都是骗人或者是无效的&#xff0c;找了一个靠谱的方法&#xff0c;亲测速度能达到10M以上&#xff0c;非常给力。 以下…