字符串相似度算法完全指南:编辑、令牌与序列三类算法的全面解析与深入分析

在自然语言处理领域,人们经常需要比较字符串,这些字符串可能是单词、句子、段落甚至是整个文档。如何快速判断两个单词或句子是否相似,或者相似度是好还是差。这类似于我们使用手机打错一个词,但手机会建议正确的词来修正它,那么这种如何判断字符串相似度呢?本文将详细介绍这个问题。

字符串相似度

当我们有两个数字时,我们可以通过从一个数字中减去另一个数字并观察结果的符号和大小来轻松比较它们。这种比较方式也可以用于向量,并且有许多方法可以做到这一点。例如常见的:余弦距离、欧几里得距离、曼哈顿距离、闵可夫斯基公式的p距离等等

但是对于字符串来说就比较复杂了,因为有时需要比较单词、句子或一般的字符串。一种简单的方法是比较字符串或单词之间的公共字母。

总的来说,有三种主要类型的算法用于衡量字符串的相似度,我们将一一介绍:

  1. 基于编辑的算法
  2. 基于令牌的算法
  3. 基于序列的算法

基于编辑的算法

基于编辑的算法,也被称为基于距离的算法,通过测量将一个字符串转换成另一个字符串所需的最少单字符操作(插入、删除或替换)数量来衡量。操作次数越多,相似度(距离)就越低(高)。这一指标为许多其他字符串相似度技术提供了基础,并广泛用于拼写检查、自动纠错和DNA序列分析。

注意:这里的每个字符和每个操作都具有相同的重要性

1、Hamming 距离

该度量标准用于测量两个等长字符串的不相似度,方法是将一个字符串叠加在另一个字符串上,并计算有多少位置的字符不同。汉明要求是长度一致的,但是一些库可以忽略长度条件,所以算法并不适用于处理长度不相同的2个字符串。

 >> import textdistance as td>> td.hamming('book', 'look')1>> td.hamming.normalized_similarity('book', 'look')0.75>> td.hamming('bellow', 'below')3>> td.hamming.normalized_similarity('Below', 'Bellow')0.5

在第一个示例中,有一个不同的字符。这使得距离等于1,归一化相似度等于(4-1)/4 = 75%。在第二个示例中,比较“bellow”和“below”,前三个字母相同,但接下来的三个字母不同。因此,距离是3,归一化相似度是(6-3)/6 = 50%。

2、Levenshtein 距离

Levenshtein 距离是最常用的基于编辑的算法,是一个字符串相似度度量标准,用于测量将一个字符串转换成另一个字符串所需的最少单字符允许操作(插入、删除或替换)的数量。它提供了一个量化的度量,表明两个字符串有多不同。它没有像Hamming 距离那样的序列长度条件。

 >> td.levenshtein('book', 'look')1>> td.levenshtein.normalized_similarity('book', 'look')0.75>> td.levenshtein('bellow', 'below')1>> td.levenshtein.normalized_similarity('Below', 'Bellow')0.84

在第一个示例中,可以通过替换一个字母来得到另一个单词,因此归一化相似度是(4-1)/4 = 75%。在第二个示例中,有一个插入操作,因此距离是1,归一化相似度是(6-1)/6 = 84%。

3、Damerau-Levenshtein距离

Damerau-Levenshtein距离是Levenshtein 距离的一个变种,它还包括了置换操作。它测量转换一个字符串到另一个字符串所需的四种操作的数量。置换涉及交换两个相邻字符。

注意:不能置换两个不相邻的符号,因此,“stop”和“spot”之间的距离不是1,而是两次(两次替换)。

 >> td.levenshtein('act', 'cat')2>> td.levenshtein.normalized_similarity('act', 'cat')0.34>> td.damerau_levenshtein('act', 'cat')1>> td.damerau_levenshtein.normalized_similarity('act', 'cat')0.67

Levenshtein需要替换两个字母‘a’和‘c’,但由于这两个字母是相邻的,我们可以使用Damerau-Levenshtein算法通过一次操作进行置换,这样就使相似度结果翻倍。这种算法在自然语言处理领域得到了广泛应用,如拼写检查和序列分析。

4、Jaro 相似度

这个算法不是一种距离测量,而是一个介于0和1之间的相似度得分。

Jaro 算法基于匹配字符的数量以及类似Damerau-Levenshtein的置换,但它没有邻近性约束。该方法使用了一个直观的公式:

只有当s1和s2中的两个字符相同且相距不超过max(|s1|, |s2|)/2 - 1个字符时,才被视为匹配。

如果没有找到匹配字符,那么这两个字符串就被认为不相似,贾罗相似度为0。但如果找到了匹配字符,那么我们就计算置换的数量。

 >> td.jaro('bellow', 'below')0.94>> td.jaro('simple', 'plesim')0>> td.jaro('jaro', 'ajro')0.92

在第一个示例中,有5个匹配字符和一个插入(这不是置换操作),因此Jaro 相似度为1/3*(5/6+5/5+6/6)。在第二个示例中,有0个匹配字符,因为共同字符不在max(|s1|, |s2|)/2-1的范围内。这就是为什么相似度为0的原因。在最后一个示例中,有4个匹配字符和第一和第二字母之间的1个置换操作,因此相似度为1/3 * (4/4+4/4+3/4) = 0.91。

5、Jaro-Winkler相似度

Jaro-Winkler相似度是Jaro相似度的一种修改。它旨在给字符串的公共前缀更多的权重。这将使得前l个字符相同的字符串得到更高的分数。其公式为:

 >> td.jaro("simple", "since")0.7>> t.jaro_winkler("simple", "since")0.76

由于两个字符串有两个共同的前缀字母。Jaro-Winkler相似度大于Jaro相似度:0.7 + 0.12(1–0.7) = 0.7 + 0.06 = 0.76。

6、Smith–Waterman相似度

Smith–Waterman算法是一种动态规划算法,用于寻找两个序列之间的最优局部对齐。与寻找最优全局对齐的Needleman-Wunsch算法不同,Smith–Waterman算法识别序列内最佳匹配的子序列,这使其比Needleman-Wunsch算法更具相关性。

该算法基于一个评分矩阵为每个可能的局部对齐分配一个分数,该矩阵定义了字符对之间的相似性或不相似性。它寻找序列中得分最大的区域,表示最佳的局部匹配。

Smith–Waterman算法在生物信息学中特别有用,用于识别生物序列中的相似区域或基序,如DNA或蛋白质序列,而不是寻找整个序列的相似性。它允许检测可能具有生物学意义的局部相似性。

 >> td.smith_waterman("GATTACA", "GCATGCU")3>> td.smith_waterman("GATTACA", "GCATGCU")0.43

基于令牌的算法

基于令牌的算法侧重于根据字符串的构成令牌或单词(但有时令牌只是字符)比较字符串。

1、Jaccard 相似度

Jaccard 距离是衡量两个集合之间相似度的一种方法。它通过比较集合中的共享元素与它们总的组合元素来量化集合的相似程度。要计算它,你需要找到交集(共享元素)的大小除以并集(所有独特元素)的大小。

 >> td.jaccard('jaccard similarity'.split(), "similarity jaccard".split())1>> td.jaccard('jaccard similarity'.split(), "similarity jaccard jaccard".split())0.66

我们可以看到,Jaccard 相似度并不考虑字符串单词的顺序。

textdistance包的实现考虑了重复元素,但使用上述公式的经典算法在第二个示例中会给出1的结果。如果你没有将序列分割成单词,算法将会将其分割成字符,并对它们应用Jaccard 公式。

如果你了解过目标检测的算法,你会常听到过一个单词 “交并比(Intersection of Union,IoU)”他其实计算的就是Jaccard 相似度。

2、Sørensen-Dice 相似度

Sørensen-Dice 相似度或系数是一种衡量两个集合之间相似度的指标,类似于Jaccard 相似度。它常用于数据分析、文本挖掘和图像处理等领域。你最常听到的一个名字是Dice系数,就是它了。

它的计算方法是找到两个集合之间共享元素(交集)数量的两倍与集合大小之和的比例。相似度的公式如下:

 >> td.sorencen('jaccard similarity'.split(), "similarity jaccard".split())1>> td.sorencen('jaccard similarity'.split(), "similarity jaccard jaccard".split())0.8

如果用集合的角度看,把相交作为正类,不相交作为负类,那么我们比较的相似度应该是预测为正类的集合与真实正类集合两者之间的相似度,换成上述四个值理解,也就是:

我们再看看f1score的计算公式

F1-score和Dice是相等的,就是这样。

Sørensen-Dice 相似度与Jaccard 相似度唯一的区别是前者更强调共享元素。

Sørensen-Dice相似度在处理二进制数据或元素的存在与否很重要的情况下特别有用。它提供了一种衡量相似度的方法,考虑了集合的大小并强调了共享元素。

它们之间的关系是:

3、Tversky 相似度

Tversky 指数是一种相似度算法,用于量化两个集合之间重叠的程度,同时考虑到假阳性和假阴性。它在处理不平衡数据或集合中元素的存在或缺失具有不同重要性的情况下特别有用。我们可以选择强调共有的词(字符)或非共享的词。

其定义公式如下:

Tversky基指数可以被视为Sørensen-Dice 和Jaccard 算法的一种泛化。

 >> td.sorencen('tversky similarity'.split(), "similarity tversky tversky".split())0.8>> tversky = td.Tversky(ks=(0.5, 0.5))>> tversky('tversky similarity'.split(), "similarity tversky tversky".split())0.8>> td.jaccard('tversky similarity'.split(), "similarity tversky tversky".split())0.67>> tversky = td.Tversky(ks=(1, 1))>> tversky('tversky similarity'.split(), "similarity tversky tversky".split())0.67>> tversky = td.Tversky(ks=(0.2, 0.8))>> tversky('tversky similarity'.split(), "similarity tversky tversky".split())0.74

Tversky基指数可以平衡集合中不同类型元素的重要性,使其适用于各种应用,如信息检索、数据挖掘和医学诊断。

4、重叠相似度

重叠系数又被称作Szymkiewicz-Simpson系数,是两个集合之间的一种简单相似度。它计算的是集合交集大小与较小集合大小的比例。重叠系数在处理二进制数据或元素的存在与否很重要的情况下特别有用。其公式如下:

这个简单的公式也意味着,如果一个集合是另一个集合的子集。

 >> td.overlap('overlap similarity'.split(), "similarity overlap overlap".split())1.0

5、余弦相似度

余弦相似度是一种广泛使用的相似度测量方法,用于量化多维空间中两个非零向量之间角度的余弦值。它通常用于比较文档、文本或其他高维数据点之间的相似度。余弦相似度捕捉向量的方向或取向,而不是它们的大小。

两个向量A和B之间的余弦相似度公式如下:

其中A.B表示向量A和B之间的点积,||A||表示向量A的欧几里得范数。

这种相似度测量结果的范围从-1(表示完全相反)到1(表示完全相同)。值为0表示正交或去相关,而介于两者之间的值表示不同程度的相似性或不相似性。

在文本匹配中,属性向量A和B通常表示文档的术语频率向量。余弦相似度作为一种机制,用于在比较时标准化文档长度。在信息检索的背景下,两个文档之间的余弦相似度范围在0到1之间,因为术语频率不能为负。即使使用TF-IDF权重,两个术语频率向量之间的角度也不会超过90°。

 >> td.cosine('cosine'.split(), "similarity".split())0>> td.cosine('cosine sim'.split(), "cosine sim sim".split())0.81

textdistance在计算余弦相似度时使用的方法与标准相似度不同。所以建议使用scikit-learn的余弦相似度计算,对于第二个示例,其结果为0.94:

A = [1, 1];B = [1, 2],所以A.B = 3,因此cos(θ) = 3/√(2*5) = 0.94。

6、N-gram相似度

N-gram比较算法是一种通过分析两个字符串中连续n个字符的子序列(称为n-gram)来衡量它们之间相似度的方法。N-gram本质上是从给定字符串中提取的长度为n的子字符串。这种算法常用于文本分析、自然语言处理和相似度比较任务。N-gram比较算法的过程包括以下步骤:

N-gram提取:将每个输入字符串分割成重叠的n个字符的序列。

计数N-gram:统计两个字符串中每个独特n-gram的出现次数。

计算相似度:比较两个字符串之间的n-gram计数,并计算相似度分数。相似度分数可以使用各种度量方法计算,常用的选项包括贾卡德相似度和余弦相似度。

让我们举一个例子,比较两个有拼写错误的单词:(trigrasm 和 trigrams)

 >> trigrams = {"tri", "rig", "igr", "gra", "ram", "ams"}>> trigrasm = {"tri", "rig", "igr", "gra", "ras", "asm"}# there are 8 unique grams in our example so# there are 4 shared words and 4 different ones>> result = 4/80.5

N-gram比较对于各种与文本相关的任务非常有用,例如文档相似度分析、抄袭检测和机器翻译。它捕捉文本中的局部模式,并可以提供关于字符串结构相似性的洞察。选择n(n-gram的长度)可以影响比较的粒度和敏感性。但是当处理短字符串时,这种算法会失效,其缺点在于该算法对不同的grams给予了更多的重视,而不是共享的grams。

基于序列的算法

基于序列的算法更侧重于分析和比较整个序列,而不是像基于令牌的算法那样比较序列中的令牌。这些算法在处理DNA序列、蛋白质序列或自然语言句子时尤其有价值。这里有一些示例性的例子:

1、Ratcliff-Obershelp相似度

Ratcliff-Obershelp相似度或Gestalt模式匹配是一种字符串相似度测量方法,侧重于基于两个字符串之间的共同子字符串来找到相似性。它特别适用于比较具有相似结构但可能在细微修改、删除或插入方面有所不同的字符串。该算法根据两个字符串之间最长公共子字符串的长度分配相似度分数。

Ratcliff-Obershelp算法的工作方式如下:

找到最长公共子字符串(LCS),在两个输入字符串之间识别最长的公共子字符串。这是通过找到在两个字符串中以相同顺序出现的公共字符序列来完成的。

然后首先移除LCS部分,并在同一位置进行分割。这将字符串分割成两部分:一部分在公共部分的左边,另一部分在右边。然后取两个字符串的左部分,再次应用此过程以找到它们的LCS。同样的过程也适用于右部分。继续递归这个过程,直到任何分割部分小于一定的设定值。

最后使用前面提到的Sorensen-Dice公式计算相似度分数。这个分数是共享字符数的两倍,除以两个字符串中的总字符数。

下面通过比较“RO pattern matching”和“RO practice”来清晰地解释这个算法。

共同的子字符串是“RO p”(4个字符)、‘a’(1个字符)、‘t’(1个字符)、‘e’(1个字符)。这意味着相似度为2*(4+1+1+1)/(19 + 11)=0.467。

但是这个算法有两个致命的缺点,第一个是它的复杂性,为O(n³),另一个是该算法不是可交换的,这意味着D(X, Y) != D(Y, X)。

对于第二个问题,我们来验证一下,第一个例子我们有D(“RO pattern matching”, “RO practice”)。让我们计算另一种方式:

 >> s1, s2 = "RO PATTERN MATCHING", "RO PRACTICE">> td.ratcliff_obershelp(s1, s2), td.ratcliff_obershelp(s2, s1), len(s1), len(s2)(0.46, 0.53, 19, 11)

共同的子字符串是“RO p”(4个字符)、‘r’(1个字符)、‘a’(1个字符)、‘c’(1个字符)、‘i’(1个字符)。这意味着相似度为2*(4+1+1+1+1)/(19 + 11)=0.53。

这个例子展示了Ratcliff-Obershelp算法不可交换性的特点,即从不同的方向比较相同的字符串对可能得到不同的相似度分数。这一性质在需要对称相似度的应用中可能是一个限制。

2、最长公共子字符串/子序列相似度

最长公共子字符串算法是一种字符串相似度算法,专注于找出两个字符串之间的最长公共子字符串。它通过识别两个字符串共享的最长连续字符序列来衡量字符串之间的相似度。相似度得分可以通过将最长公共子字符串的长度除以最长字符串的长度来计算。

与子字符串不同,子序列不要求在原始序列中占据连续位置。因此,最长公共子序列总是大于最长公共子字符串。相似度得分的计算方式与后者相同。

以下示例:

 >> s1, s2 = "RO PATTERN MATCHING", "RO PRACTICE">> td.lcsstr(s1, s2), td.lcsseq(s2, s1), td.lcsseq(s2, s1)('RO P', 'RO PRATC', 'RO PRACI')>> td.lcsstr.normalized_similarity(s1, s2), td.lcsseq.normalized_similarity(s1, s2)(0.21, 0.42)

在上述示例中可以看到最长公共子字符串总是具有连续字符的子字符串,但在最长公共子序列中没有这个约束。

(A, B)的最长公共子序列与(B, A)不同。实际上可以在上面的例子中看到不同的结果(RO PRATC)和(RO PRACI)。

总结

在本文中,我们深入探讨了三个基本算法类别:基于编辑的算法、基于令牌的算法和基于序列的算法。需要记住的重要点是:

基于编辑的算法:

  • Damerau-Levenshtein和Jaro winkler距离通过编辑操作量化字符级相似度。
  • 适合强调单字符更改和更正的应用。
  • 对于拼写检查、光学字符识别、字符精确度和文本自动纠正非常有价值。

基于令牌的算法:

  • Jaccard 和Sørensen-Dice相似度关注令牌集,忽略序列。
  • 对于文档聚类、抄袭检测和推荐系统有效。
  • 强调存在而非顺序,有助于分类。
  • 可用于内容分类和文档比较。

基于序列的算法:

  • 最长公共子序列(LCS)和Ratcliff/Obershelp方法保留顺序。
  • 对于DNA序列对齐、文本近重复检测和版本控制至关重要。
  • 提供字符串内部结构关系的洞察。

没有单一的方法适用于所有情景,每一类算法都有其优势,适应特定的需求和背景。所以我们总结了这些算法,一个具备这些多样化技术知识的优秀数据科学家可以利用它们的综合力量来解决字符串相似度分析中的广泛挑战。

作者:Yassine EL KHAL

https://avoid.overfit.cn/post/43c11a3fee684fecb81eebf5647159aa

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

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

相关文章

如何为老化的汽车铅酸电池充电

一项小研究表明,汽车铅酸电池不同于深循环或固定电池。汽车电池旨在限度地提高启动电流容量,并且对深度放电或浮充(也称为第 3 阶段充电循环)反应不佳。起动电池的极板结构使表面积化,并且电解液比重 (SG) 高于其他电池,以提供高启…

C# 实现位比较操作

1、目标 对两个字节进行比较,统计变化位数、一位发生变化的位数、二位发生变化的位数、多位发生变化的位数。 2、代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Lin…

O2OA(翱途) 开发平台之HTTP端口规划

O2OA(翱途) 开发平台[下称O2OA开发平台或者O2OA]采用相对灵活的系统架构,支持三种服务器运行的方式。本篇主要阐述合并服务运行独立服务运行代理端口运行三种服务器运行方式。 一、先决条件: 1、O2Server服务器正常运行,系统安装部署请参考文…

Vue84-Vuex的工作原理与搭建开发环境

一、vuex工作原理 stats:是一个object对象,里面有很多key-value,存放的就是要操作的数据。mutations:是一个object对象,真正去操作stats的人。actions的作用:是一个object对象,当一个动作对应的…

【Spring Boot】关系映射开发(一):一对一映射

关系映射开发(一):一对一映射 1.认识实体间关系映射1.1 映射方向1.2 ORM 映射类型 2.实现 “一对一” 映射2.1 编写实体2.1.1 新建 Student 实体2.1.2 新建 Card 实体 2.2 编写 Repository 层2.2.1 编写 Student 实体的 Repository2.2.2 编写…

从涟漪到波浪:资产代币化的变革力量

原文标题:《From ripples to waves: The transformational power of tokenizing assets》撰文:Anutosh Banerjee,Matt Higginson,Julian Sevillano,Matt Higginson编译:Chris,Techub News本文来…

还是NC,项目代码开源|scRNA+bulkRNA+因子分析验证地塞米松治疗Covid19

说在前面 平时发文章的话,做药物用的大多都是仅仅是GEO的bulkRNA,有人的有鼠的,然后做做流水线分析,最后面PCR。今天看一篇发NC的工作量,怎么用转录组分析做药物的转化免疫学 这篇文章作者已经上传Github了&#xff…

LabVIEW自动探头外观检测

开发了一套基于LabVIEW的软件系统,结合视觉检测技术,实现探头及连接器外观的自动检测。通过使用高分辨率工业相机、光源和机械手臂,系统能够自动定位并检测探头表面的细微缺陷,如划痕、残胶、异色、杂物等。系统支持多种探头形态&…

【C++ OpenCV】机器视觉-二值图像和灰度图像的膨胀、腐蚀、开运算、闭运算

原图 结果图 //包含头文件 #include <opencv2/opencv.hpp>//命名空间 using namespace cv; using namespace std;//全局函数声明部分//我的腐蚀运算 Mat Erode(Mat src, Mat Mask, uint32_t x0, uint32_t y0) {uint32_t x 0, y 0;Mat dst(src.rows, src.cols, CV_8U…

如何在忘记密码的情况下重置Realme手机?

欢迎阅读我们关于如何在有或没有密码的情况下重置Realme手机的综合指南。无论您是忘记了密码&#xff0c;还是只是需要将设备恢复到出厂设置&#xff0c;我们都会为您提供所需的专业提示和技术专长。 发现分步说明、专家提示和行之有效的方法&#xff0c;轻松重新控制您的 Rea…

Hadoop3:集群压测-读写性能压测

一、准备工作 首先&#xff0c;我们要知道&#xff0c;平常所说的网速和文件大小的MB是什么关系。 100Mbps单位是bit&#xff1b;10M/s单位是byte ; 1byte8bit&#xff0c;100Mbps/812.5M/s。 测试 配置102、103、104虚拟机网速 102上用Python开启一个文件下载服务&#x…

Alpha2:使用深度强化学习挖掘公式化的超额收益因子(附论文及源代码)

原创文章第577篇&#xff0c;专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 今天说说因子挖掘&#xff0c;我们之前交付的Deap遗传算法因子挖掘&#xff0c;大家可以前往温习一下&#xff1a; 源码发布Quantlab4.2&#xff0c;Deap因子挖掘|gplearn做不到的…

K8S学习教程(二):在 PetaExpress KubeSphere容器平台部署高可用 Redis 集群

前言 Redis 是在开发过程中经常用到的缓存中间件&#xff0c;为了考虑在生产环境中稳定性和高可用&#xff0c;Redis通常采用集群模式的部署方式。 在制定Redis集群的部署策略时&#xff0c;常规部署在虚拟机上的方式配置繁琐并且需要手动重启节点&#xff0c;相较之下&#…

十款绚丽的前端 CSS 菜单导航动画

CSS汉堡菜单是一种非常流行的PC端和移动端web菜单风格&#xff0c;特别是移动端&#xff0c;这种风格的菜单应用更为广泛。这款菜单便非常适合在手机App上使用&#xff0c;它的特点是当顶部菜单弹出时&#xff0c;页面内容将会配合菜单出现适当的联动&#xff0c;让整个页面变得…

关于linux捕捉鼠标事件的方法

网上找了很多方法&#xff0c;都比较杂乱。这篇文章专注于读取鼠标的动作&#xff1a;左键、右键、中键、滚轮。 linux的设备都以文件形式存放&#xff0c;要读取鼠标&#xff0c;有两种方法&#xff0c;一种是通过/dev/input/mice&#xff0c;一种是通过/dev/input/eventx (x…

探索线程安全:HashMap 的四种使用技巧

这篇文章&#xff0c;我们聊聊线程安全使用 HashMap 的四种技巧。 1 方法内部&#xff1a;每个线程使用单独的 HashMap 如下图&#xff0c;tomcat 接收到到请求后&#xff0c;依次调用控制器 Controller、服务层 Service 、数据库访问层的相关方法。 每次访问服务层方法 serv…

vue H5页面video 视频流自动播放, 解决ios不能自动播放问题

视频组件 <videostyle"width: 100%; height: 100%;object-fit: fill"class"player"refplayer_big_boxcontrolspreloadautoplay //自动播放muted //是否静音playsinline"true"x5-playsinline""webkit-playsinline"tru…

[Linux安全运维] Linux用户以及权限管理

Linux用户以及权限管理 Linux用户和组 用户信息文件pasawd /etc/passwd文件用于存储用户的信息 :用于分割不同的字段信息 字段示例&#xff08;第一行&#xff09;含义说明1root用户名2x密码占位符x代表用户有密码存储在shadow文件中无内容代表用户登录系统不需要密码30UID…

前端三件套开发模版——产品介绍页面

今天有空&#xff0c;使用前端三件套html、css、js制作了一个非常简单的产品制作页面&#xff0c;与大家分享&#xff0c;希望可以满足大家应急的需求。本页面可以对产品进行“抢购”、对产品进行介绍&#xff0c;同时可以安排一张产品的高清大图&#xff0c;我也加入了页面的背…

JAVA实现二分查找,斐波那契数列,深度优先搜索详情教程【包含代码】

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…