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

news2024/11/27 0:25:05

请想象一个情景:

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

打开我们浏览器的搜索框,输入你想的这个词,然后点击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中的位置,否则返回-1
	if (j == pLen)
		return i - j;
	else
		return -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;
	else
		return -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.coloradmin.cn/o/1625725.html

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

相关文章

常用的时间序列分析方法总结和代码示例

时间序列是最流行的数据类型之一。视频&#xff0c;图像&#xff0c;像素&#xff0c;信号&#xff0c;任何有时间成分的东西都可以转化为时间序列。 在本文中将在分析时间序列时使用的常见的处理方法。这些方法可以帮助你获得有关数据本身的见解&#xff0c;为建模做好准备并…

删除docker的容器与镜像

如果您想要卸载通过 docker pull influxdb 命令下载的 InfluxDB 容器&#xff0c;您需要执行以下步骤&#xff1a; 1. **停止正在运行的 InfluxDB 容器**&#xff1a; 首先&#xff0c;您需要停止任何正在运行的 InfluxDB 容器。您可以使用以下命令来查找正在运行的 InfluxD…

15 JavaScript学习:循环

JavaScript循环语句概念和分类 JavaScript中的循环语句用于重复执行特定的代码块&#xff0c;直到指定的条件不再满足。下面是JavaScript中常用的循环语句以及它们的概念和分类&#xff1a; for 循环&#xff1a;for循环通过指定起始条件、循环条件和递增/递减步长来进行迭代。…

C++程序在Windows平台上各种定位内存泄漏的方法

一、前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏&#xff0c;因为Linux在开发领域的使用场景大多是跑服务器&#xff0c;再加上它的开源属性&#xff0c;相对而言&#xff0c;处理问题容易形成“统一”的标准。而在Windows平台&#xff0c;服务器和客户端…

云计算时代:SFP、SFP+、SFP28、QSFP+和QSFP28光纤模块详解

随着数据中心的快速发展和云计算的广泛应用&#xff0c;高速、高效率的光纤网络传输成为关键需求。在众多光纤模块中&#xff0c;SFP、SFP、SFP28、QSFP和QSFP28是最常见的几种类型。本文将为您详细解析这几种光纤模块之间的区别&#xff0c;帮助您更好地了解和选择适合自己需求…

VS2019中配置C++ OpenCV 4.5.4完整指南

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

C# 图像处理 添加水印

方法1&#xff0c;使用自带的画刷进行绘制水印 示例代码 public partial class Form1 : Form{public Form1(){InitializeComponent();}string photoPathstring.Empty;Bitmap image null;private void button1_Click(object sender, EventArgs e) //选择照片{OpenFileDialog d…

JavaWeb-自学JSP组件报告

自学JSP组件报告 一、组件资源及作用 1. commons-fileupload-1.2.2.jar 组件作用&#xff1a;用于处理HTTP文件上传请求&#xff0c;提供了文件上传的解析和存储功能。它允许开发者在Web应用中轻松实现文件上传功能。 2. commons-io-2.4.jar 组件作用&#xff1a;提供了一…

免单优选:电商销售新模式,低门槛高回报引领消费热潮

免单优选&#xff0c;一种颠覆传统的电商销售新模式&#xff0c;其核心在于通过价格优化策略、创新的奖励机制以及社交网络的深度应用&#xff0c;来激发消费者的购买欲望&#xff0c;进而推动销售实现快速增长。 一、坚守法律底线&#xff0c;打造透明奖励体系 免单优选模式坚…

POCEXP编写—POC编写实战

POC&EXP编写—POC编写实战 1. 概念引入2. POC编写2.1. GET与POST2.2. thinkcmf文件包含(Get请求)2.2.1. POC验证2.2.1.1. 漏洞存在复现 2.2.2. 编写POC2.2.2.1. 脚本报错2.2.2.2. 脚本美化2.2.2.3. 脚本测试2.2.2.4. 注意事项 2.3. Struts2 S2-061 远程命令执行漏洞(Get请求…

Hot 100总结【leetcode】

文章目录 哈希1. 1 两数之和2. 49 字母异或词分组3. 128 最长连续序列4. 283 移动零5. 11 盛最多水的容器6. 15 三数之和7. 42 接雨水 滑动窗口8. 3 无重复的最长子串9. 438 找到字符串中所有字母异位词子串10. 560 和为k的子数组11. 239 滑动窗口最大值12. 76 最小覆盖子串 普…

SpringBoot + kotlin 协程小记

前言&#xff1a; Kotlin 协程是基于 Coroutine 实现的&#xff0c;其设计目的是简化异步编程。协程提供了一种方式&#xff0c;可以在一个线程上写起来像是在多个线程中执行。 协程的基本概念&#xff1a; 协程是轻量级的&#xff0c;不会创建新的线程。 协程会挂起当前的协…

KT-0850 三箱社交箱

01简介 社交一般指社会交往。 社会交往 &#xff0c;简称“社交”&#xff08;socializing&#xff09;&#xff0c;是指在一定的历史条件下&#xff0c;个体之间相互往来&#xff0c;包括物质、精神交流的社会活动。三箱社交(Three-chamber Sociability)箱行为实验是一种经典…

【蓝桥2025备赛】容斥原理

容斥原理 背景&#xff1a;两个集合相交 高中的韦恩图&#xff0c;我们知道两个集合相交时我们可以通过简单的计算来认识相关的性质 集合相交的区域是 A ∩ B A\cap B A∩B ,集合的并集是 A ∪ B A\cup B A∪B ,那怎么用集合表示 A ∪ B A\cup B A∪B 我们可以看作是A集合…

【go零基础】go-zero从零基础学习到实战教程 - 2项目初始化

到项目初始化过程了&#xff0c;这边的项目设计完全按照作者自己的喜好来进行定义和设置的&#xff0c;所以各位完全可以按照自己的偏好自喜设置哈。 首先是创建一个工作文件夹哈。 别问为啥不直接quickstart&#xff0c;因为quickstart生成的api名字是greet&#xff0c;改起来…

Springboot3集成Web、RedisTemplate、Test和knife4j

本例将展示&#xff0c;如何在Springboot3中完成&#xff1a; Redis功能的Web接口实现构建Redis功能的单元测试knife4j自动化生成文档 Redis功能 Pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter…

02 贪吃蛇

前言 呵呵 这是不知道 在哪里看到的 别人做的一个贪吃蛇 因此 也把我 之前的 贪吃蛇 移植上来了 当然 这个不过是为了 简单的入门了解, 呵呵 然后 c版本的贪吃蛇 需要先移植成 c 版本, 然后 再根据 单片机相关 设计调整 比如 led 点阵的输出, 比如 c99 语法的一些不兼容…

分布式密钥生成

可验证且无经销商 分布式密钥生成 (DKG) 是一种加密协议&#xff0c;使多方能够协作生成共享密钥&#xff0c;而无需任何一方完全了解密钥。 它通过在多个参与者之间分配信任来增强各种应用程序的安全性&#xff0c;从而降低密钥泄露的风险。 我们引入了一种可验证且无经销商的…

C语言中整型与浮点型在内存中的存储

今天让我们来看看整型的数据和浮点型的数据在内存中是怎么存储的呢 整型数据在内存中的存储 整型数据在内存中存储的是二进制的补码 正数的话也没什么可说的&#xff0c;原码反码补码都相同 我们来看看负数&#xff1a; 以-5为例 原码&#xff1a;10000000 00000000 00000000 0…

集控中心的调度桌成就事业的重要工具

在集控中心的调度室内&#xff0c;一排排整齐的调度桌构成了一道独特的风景线。其中最引人注目的便是那一张张配备现代化设备&#xff0c;宽敞而舒适的调度桌。就我们嘉德立来说这些调度桌不仅见证了调度员们严谨、细致的工作态度&#xff0c;也是他们成就事业的重要工具。 集控…