数据结构(六)----串

news2024/11/19 8:48:39

目录

1.串的定义

2.串的基本操作

3.串的存储结构

(1)串的定义

•顺序存储

•链式存储

(2)求串长

(3)求子串

(4)比较串的大小

(5)定位操作

4.字符串的模式匹配

(1)朴素模式匹配算法

(2)KMP算法

•求模式串中的next数组(重点)

•练习:

(3)KMP算法的进一步优化

•求nextval数组的方法(重点)


1.串的定义

串,即字符串(String)是由零个或多个字符组成的有限序列。一般记为 S='a_{1}a2_{2}.....a_{n}'。其中,S是串名,单引号括起来的字符序列是串的值(有的地方用双引号(如Java,C),有的地方用单引号(如Python));ai可以是字母、数字或其他字符;串中字符的个数n称为串的长度。n=0时的串称为空串(用\phi表示)。

几个专业术语

子串:串中任意个连续的字符组成的子序列(空串也是字符串的子串)。

主串:包含子串的串。

字符在主串中的位置:字符在串中的序号(从1开始)。

子串在主串中的位置:子串的第一个字符在主串中的位置(从1开始)。

空串与空格串:

空串:单引号之间没有任何字符,如下面的M。

空格串:有多个空格字符组成的字符串,每个空格字符占1B,如下面 的N。

是一种特殊的线性表,数据元素之间呈线性关系。线性表中存放的数据对象可以是各种类型的,但是串的数据对象限定为字符集(如中文字符、英文字符、数字字符、标点字符等),串的基本操作,如增删改查等通常以子串(多个数据对象)为操作对象。

2.串的基本操作

StrAssign(&T,chars):赋值操作。把串T赋值为chars。

StrCopy(&T,S):复制操作。由串S复制得到串T。

StrEmpty(S):判空操作。若S为空串,则返回TRUE,否则返回FALSE

StrLength(S):求串长。返回串S的元素个数。

ClearString(&S):清空操作。将S清为空串。

DestroyString(&S):销毁串。将串S销毁(回收存储空间)。

Concat(&T,S1,S2):串联接。用T返回由S1和S2联接而成的新串

SubString(&Sub,S,pos,len):求子串。用Sub返回串S的第pos个字符起长度为len的子串。

Index(S,T):定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0。

StrCompare(S,T):比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。

比较规则

计算机只能存储二进制数,需要确定一个编码的规则,使得各个字符与二进制数能够一一对应起来,比较字符就是在比较二进制数的大小,例如'a'的Ascll码是97,对应的二进制就是

0110 0001,'A'的Ascll码是65,对应的二进制为0100 0001。

Ascll码表可以看:ASCII 表 | 菜鸟教程 (runoob.com)

Ascll字符集只是英文字符的集合。但是,除了英文字符外,还有很多其他字符(例如:中文,法语等),Ascll字符集中无法包含所有的字符,因为Ascll字符集中,每个字符占用的空间为1B(8bit的二进制数),只能表示2的8次方,即256个状态。

所以出现了其他字符集,如Unicode字符集,包含了中英文字符的集合。这些字符通过一个编码方案映射为二进制数(一个字符集可以有多个编码方案,例如UTF-8和UTF-16就是两种不同的Unicode编码方案)。

注:采用不同的编码方式,每个字符所占空间不同,考研中只需默认每个字符占1B即可。

1.从第一个字符开始往后依次对比,先出现更大字符的串就更大 。

2.长串的前缀与短串相同时,长串更大。

3.只有两个串完全相同时,才相等。

3.串的存储结构

(1)串的定义
•顺序存储
//定长顺序存储
#define MAXLEN 255    //预定义最大串长为255
typedef struct{
    char ch[MAXLEN];    //每个分量存储一个字符
    int length;    //串的实际长度
}SString;

//堆分配存储(用malloc函数申请的内存空间属于内存的堆区)
typedef struct{
    char *ch;    //按串长分配存储区,ch指向串的基地址
    int length;    //串的长度
}HString;

HString S;
S.ch=(char *)malloc(MAXLEN * sizeof(char));
S.length=0;

//由malloc申请的内存空间需要手动free回收,而用静态数组申请的空间是由系统自动回收的。

顺序存储的实现:

在方案一中,专门声明一个int型变量length表示字符串的长度。

在方案二中,若用ch[0]来存储字符串的长度,那么字符的位序和数组下标相同。但是一个字符只占8bit,其所能表示的数字范围为0~255。

在方案三中没有Length变量,以字符’\0'表示结尾(对应ASCIl码的 0),采用这种方式,若想知道字符串的长度,需要从头到尾扫描,扫描到‘\0’才能得到字符串的长度。若经常访问字符串的长度,那么这种方案就是不太可取的。

方案四中采用了方案1与方案2的结合,即舍弃ch[0]不用,并且专门声明一个int型的变量length表示字符串的长度。下面顺序存储的代码编写都是以方案四为存储结构的。

•链式存储
typedef struct StringNode{
    char ch;    //每个结点存1个字符
    struct StringNode * next;
}StringNode,* String;

一个char的大小为1B,但是在32位计算机中,指针大小为4B,这就表示我们在存储一个字符时,只用了1B存储实际需要存储的信息,用了4B存储了辅助信息。这样存储密度就太低了。

如何解决?

typedef struct StringNode{
    char ch[4];    //每个结点存多个字符
    struct StringNode * next;
}StringNode,* String;

:若最后一个结点的字符存不满,那么可以用一些特殊的字符填充进去,如'#'或'\0'。

(2)求串长
int StringLength(SString S){
    return S.length;
}
(3)求子串

SubString(&Sub,S,pos,len):求子串。用Sub返回串S的第pos个字符起长度为len的子串。

bool SubString(String &Sub,SString S,int pos,int len){
    if(pos+len-1>S.length)
        return false;    //子串范围越界
    for(int i=pos;i<pos+len;i++)
        Sub.ch[i-pos+1]=S.ch[i];
    Sub.length=len;
    return true;

}

(4)比较串的大小

StrCompare(S,T):比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。

int StrCompare(SString S,SString T){
    for(int i=1;i<=S.length && i<T.length;i++){
        if(S.ch[i]!=T.ch[i])
            return S.ch[i]-T.ch[i]; 
    }
    //扫描过的所有字符都相同,则长度长的串更大
    return S.length-T.length;
}

(5)定位操作

Index(S,T):定位操作。若主串S中存在与串T值相同的子串,则返回它在主串s中第一次出现的
位置;否则函数值为0。

int StringLength(SString S){
    return S.length;
}

bool SubString(String &Sub,SString S,int pos,int len){
    if(pos+len-1>S.length)
        return false;    //子串范围越界
    for(int i=pos;i<pos+len;i++)
        Sub.ch[i-pos+1]=S.ch[i];
    Sub.length=len;
    return true;
}

int StrCompare(SString S,SString T){
    for(int i=1;i<=S.length && i<T.length;i++){
        if(S.ch[i]!=T.ch[i])
            return S.ch[i]-T.ch[i]; 
    }
    //扫描过的所有字符都相同,则长度长的串更大
    return S.length-T.length;
}

int Index(SString S,SString T){
    int i=1,n=StrLength(S),m=StrLength(T);
    SString sub;    //用于暂存子串
    while(i<=n-m+1){
        SubString(sub,S,i,m);
        if(StrCompare(sub,T)!=0)    ++i;
        else return i;    //返回子串在主串中的位置
    }
    return 0;    //S中不存在与T相等的子串 
}

4.字符串的模式匹配

字符串模式匹配就是在主串(被查找串)中找到与模式串(想要查找的字符串)相同的子串,并返回其所在位置。

子串:主串的一部分,一定存在

模式串:不一定在主串中找到

(1)朴素模式匹配算法

若主串长度为n,模式串长度为 m,将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串或所有的子串都不匹配为止。

长度为n的主串中,最多有n-m+1个子串

上面说的定位操作,即Index(S,T),就是一种朴素模式匹配算法:

int Index(SString S,SString T){
    int i=1,n=StrLength(S),m=StrLength(T);
    SString sub;    //用于暂存子串
    while(i<=n-m+1){
        SubString(sub,S,i,m);
        if(StrCompare(sub,T)!=0)    ++i;
        else return i;    //返回子串在主串中的位置
    }
    return 0;    //S中不存在与T相等的子串 
}

也可以直接通过数组下标实现朴素模式匹配算法

设置两个扫描指针i,j,将两个指针指向的字符依次对比,若指针指向的字符相同,就将指针一起后移。

若匹配失败,如上图所示,则主串指针 i 指向下一个子串的第一个位置,模式串指针 j 回到模式串的第一个位置。

 

只需要改变i,j指针的指向,就可以完成以上操作。

i=i-j+2;    //i-(j-1)相当于i回到开始的位置,再加1就是下一个字符串
j=1;    //j-(j-1)就相当于回到了1

在下一个串中匹配失败,i-j+2 指向下一个串,j继续重新指回1

若j>T.length(j指向的位置超出了模式串的长度),则当前子串匹配成功,返回当前子串第一个字符的位置 ---- i-T.length

int Index(SString S,SString T){
    int i=1,j=1;
    while(i<=S.length && j<=T.length){
        if(S.ch[i]==T.ch[i]){
            ++i;++j;    //继续比较后继字符
        }
        else{
            i=i-j+2;
            j=1;    //指针回退重新开始匹配
        }
    }
    if(j>T.length)
        return i-T.length;
    else
        return 0;
}

设主串长度为n,模式串长度为m,那么最坏时间复杂度=O(nm)

若主串和模式串如下图所示, 最坏的情况,每个子串都要对比 m 个字符,共 n-m+1 个子串,复杂度=O((n-m+1)m) = O(nm-m^2+m) = O(nm),通常情况下n>>m

(2)KMP算法

在朴素模式匹配算法中,若主串与模式串不匹配,那么i就要回退到2,j回退到1。换个角度也可以理解为,将模式串往右移动了一步。

匹配失败,模式串继续右移。朴素模式匹配算法的缺点就是,当某些子串与模式串能部分匹配时,主串的扫描指针 i 经常回溯,导致时间开销增加。

所以KMP算法针对这一点进行了改进,即主串指针不回溯,只有模式串指针回溯。

若扫描到不匹配的字符,那么就将这个字符与模式串的第一个字符进行比较,即 i 指向的位置不变,j指回1。也可以理解为模式串往后移动了5步,相比于朴素匹配算法中模式串一步一步右移,这个算法的效率提高了很多。

1.若这个字符也为g,那么就让i++,j++,也就是继续匹配后一个字符。

2.若这个字符不为j,那么就 i++,j依然保持1

3.若扫描到 j =5,才发现匹配失败,也就是 i指向的字符不是“l”。

也就是以“g”开头的字符串与模式串是不匹配的,同理,因为之前扫描过的“o”“o”,都匹配上了(不匹配的字符之前,主串的字符一定是和模式串一致的),那么以“o”开头的字符串肯定也匹配不上。

但是以g开头的子串是可能与模式串进行匹配的,但是在代码中只是判断了i指向的字符与“l”是不匹配的,所以我们可以间接判断,i指向的字符是不是“o”,因为“g”后面是“o”。

所以若j=5 时发生不匹配,则应让 j 回到 2。

4.若 j=4 时发生不匹配,则应让 j 回到1。但是其实这里 i 指向字符一定不是 “g” 了

那么让j回到1,继续重新匹配,就会在开头进行一次没有必要的对比

但是相比于朴素匹配算法而言,他已经少对比了2次了。改进方法在KMP算法的改进中会讲。

5.若 j=3 时发生不匹配,则应让j回到1

相比于朴素算法而言,少进行了一次对比

6.若j=2时发生不匹配,则应让 j 回到1

总结:

当j=k且发现字符不匹配时,令j=next[k]。例如,当j=5发现不匹配时,那么就让j=next[5],即2。

注:当j=1,即第1个字符匹配失败时,先令j=0,再进行i++,j++,就可以使i往后扫描字符,而j保持1。看代码应该能更好理解。

所以KMP算法的关键根据模式串的内容设计出合适的数组next。next数组只和模式串有关,和主串没有关系,也就是说只要模式串不变,不管主串是什么,next数组都是一样的。

代码如下:

int Index_KMP(SString S,SString T,int next[]){
    int i=1,j=1;
    while(i<=S.length && j<=T.length){
        if(j==0 || S.ch[i]==T.ch[j])    
//刚开始j=1,若j=1时,两个字符不匹配,那么跳转到else
//else中,j=next[j],j=next[1]=0,若j=0,那么++i,让i指针向后扫描,并且让j指针保持1
        {
            ++i;
            ++j;    //继续比较后继字符

        }
        else
            j=next[j];    //模式串向右移动
    }
    if(j>T.length)
        return i-T.length;    //匹配成功
    else
        return 0;

}

相比于朴素模式匹配,KMP算法修改了两个部分,当主串和模式串不匹配时,不需要主串指针i回溯,只需要修改模式串中指针 j 即可。

•求模式串中的next数组(重点)

当模式串的第 j 个字符匹配失败时,令模式串跳到 next[j] 再继续匹配。

如下图所示,若j=6时不匹配,应该让next[6]=3,即让 j 指向3,或者说模式串向右移动3位。对比主串中i指向的字符和c是否能匹配。

同理,当j=7时,匹配失败,将模式串往右移动2位,让j=5,看i指向的字符能否与"a"匹配(next[7]=5)。

若刚开始i指向的字符“a”与“c”不匹配,并且将模式串直接向右移动了4位,即next[7]=3,如下图所示:

那么继续往后检查,当i指向字符“c”时,发现a,c并不匹配。

但是按照刚开始的方案,即符号串往右移动2位,那么字符应该是匹配的。

所以只要模式串中有更长的部分能匹配,那么就只考虑更长的情况:

特殊的情况上面已经说过了,即j=1时匹配失败,那么只能紧接着匹配下一个子串,先令j=0,然后让i++,j++,i向后继续扫描,而j=1。所以对任何一个字符串而言,next[1]都为0。

我们将以下红框部分称为串的前缀包含第一个字符,且不包含最后一个字符的子串。

串的后缀包含最后一个字符,且不包含第一个字符的子串。

得出结论:当第 j 个字符匹配失败,由前1~j-1个字符组成的串记为S,则:next[j]= S的最长相等前后缀长度+1。并且next[1]是固定为0的。

例如上图,最长相等前后缀=4,那么next[j]=4+1=5。

练习

模式串:‘ababaa’

1.next[1]=0

2.next[2]表示当匹配到第2个字符时匹配失败,在第2个字符之前组成的串'a'。'a'不存在前缀和后缀,所以其最长前后缀长度=0,所以也可以总结next[2]一定是1

3.next[3]表示当匹配到第3个字符时匹配失败,第3个字符之前组成的串是'ab',其最长相等前后缀长度也为0,所以next[3]=1

4.同理第4个字符前面的字符串“aba",其最长相等前后缀长度为1,所以next[4]=1+1=2

5.第5个字符前面的字符串“abab”,所以next[5]=2+1=3

6.第6个字符前面的字符串为“ababa”,所以next[6]=3+1=4

所以模式串“ababaa”的next数组如下:

代码实现如下:

//求模式串T的next数组
void get_next(SString T,int next[]){    T为模式串
    int i=1,j=0;
    next[1]=0;
    while(i< T.length){   
        if(j==0 || T.ch[i]=T.ch[j]){    
            ++i; ++j; 
    //若pi=pj,则next[j+1]=next[j]+1  
            next[i]=j;
        }
        else
            //否则令j=next[j],循环继续
            j=next[j];
    }
}
//while循环中i<T.length,也就是i的范围是1~m,所以可以认为这个函数的时间复杂度为O(m)

//KMP算法
int Index_KMP(SString S,SString T){
    int i=1,j=1;
    int next[T.length+1];
    get_next(T,next);    //求模式串的next数组
    while(i<=S.length && j<=T.length){
        if(j==0 || S.ch[i]==T.ch[j])    
        {
            ++i;
            ++j;    //继续比较后继字符
        }
        else
            j=next[j];    //模式串向右移动
    }
    if(j>T.length)
        return i-T.length;    //匹配成功
    else
        return 0;
}
//在KMP算法中,i由1~n,再加上调用的get_next函数,所以KMP算法的时间复杂度为O(n+m)

朴素模式匹配算法中,当某些子串与模式串能部分匹配时,主串的扫描指针 i 经常回溯,导致时间开销增加。最坏时间复杂度O(nm)。而KMP算法中,根据模式串T求出next数组,当子串和模式串不匹配时,利用next数组进行匹配,主串指针 i 不回溯,模式串指针 j=next[j] 。算法最坏时间复杂度:O(n+m),其中,求 next 数组时间复杂度O(m),模式匹配过程最坏时间复杂度 O(n)

如果不会经常出现子串与模式串部分匹配问题,那么KMP算法就显先不出很大的优势。

(3)KMP算法的进一步优化

KMP算法是通过next数组进行匹配的。下面例子中,j指向的字符是a,那么说明主串中的第3个字符一定不是a。

当j=3的字符与主串不匹配时,让j指针指向1,那么j指向的字符a与主串中第3个字符肯定是不匹配的。

所以当j=1的字符与匹配失败的字符相等时,直接让next[3]=0,跳过中间next[3]--->next[1]--->0这一步。

同理:

当j=5时,模式串与主串的值不匹配,即主串的第5个字符不是"b",那么让next[5]=2。

但是由于第2个字符与匹配失败的这一字符相等,所以让 j 指向第2个位置后,接下来的匹配一定是不成功的。这次匹配失败后,next[2]=1,让j=1。

所以可以直接跳过注定匹配失败的那一步,直接让next[5]=1,这样就能少做一次对比。

所以总结下来,如果next数组所指的字符与原本失配的字符相等的话,就可以对next数组优化,如果不等的话,则next数组的值是不变的。

在KMP算法的优化中,我们只优化了next数组,所以在进行KMP算法中,将nextval[]数组替换next[]数组即可。

•求nextval数组的方法(重点)

若模式串ababaa的next数组如下图所示:

nextval[1]=0;        //nextval[1]的值直接写0

如果next[j]所指的字符,与j所指的字符不相同,就让nextval[j]=next[j],例如下图,当前j所指的字符是b,而其next[2]所指的字符是a,两者不同,那么nextval[2]=1 不变。

如果j指向的字符与next[j]所指的字符相同,例如下图,j=3时,j所指的字符是a,其next[3]所指的字符也是a,那么就让nextval的值优化为next[1]的值0。

nextval[j]=nextval[next[j]];        //nextval[3]=nextval[next[3]]

以此类推:

j=5时指向的是a,next[5]也是a,那么其nextval[5]等于0,不再等于1了,根据已经优化的数组来。

最后得到:

代码如下:

nextval[i]=0;
for (int j=2;i<=T.length;j++){
    if(T.ch[next[j]]==T.ch[j])
        nextval[j]=nextval[next[j]];
    else
        nextval[j]=next[j];
}

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

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

相关文章

第四百六十回

文章目录 1. 概念介绍2. 方法与细节2.1 获取方法2.2 使用细节 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取当前系统语言"相关的内容&#xff0c;本章回中将介绍如何获取时间戳.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

适配器模式:连接不兼容接口的桥梁

在软件开发中&#xff0c;适配器模式是一种结构型设计模式&#xff0c;它允许不兼容的接口之间进行交互&#xff0c;从而使它们能够一起工作。这个模式经常用于系统升级或集成第三方库的时候&#xff0c;当现有的代码无法直接使用新系统或库提供的接口时&#xff0c;适配器可以…

基于Java+Vue的中国咖啡文化宣传网站(源码+文档+包运行)

一.系统概述 本课题是根据咖啡文化宣传需要以及网络的优势建立的一个中国咖啡文化宣传网站&#xff0c;来实现中国咖啡文化宣传以及咖啡商品售卖的功能。 本中国咖啡文化宣传网站应用Java技术&#xff0c;MYSQL数据库存储数据&#xff0c;基于SSMVue框架开发。在网站的整个开发…

【QT入门】Qt自定义控件与样式设计之自定义QLineEdit实现搜索编辑框

往期回顾 【QT入门】Qt自定义控件与样式设计之qss的加载方式-CSDN博客 【QT入门】Qt自定义控件与样式设计之控件提升与自定义控件-CSDN博客 【QT入门】Qt自定义控件与样式设计之鼠标相对、绝对位置、窗口位置、控件位置-CSDN博客 【QT入门】Qt自定义控件与样式设计之自定义QLin…

找不到mfc110u.dll怎么办,总结5种有效的解决方法

在日常操作计算机的过程中&#xff0c;我们时常会遭遇各类突发状况&#xff0c;其中一种颇为常见的问题便是当试图运行某个特定软件时&#xff0c;系统突然弹出一则令人困扰的错误提示&#xff1a;“由于找不到mfc110u.dll&#xff0c;无法继续执行代码”。这个问题通常是由于缺…

Vol.44 一个分享网站的网站,每个月8.7万访问量

哈咯&#xff0c;各位朋友好啊&#xff0c;我是欧维&#xff0c;今天要给大家分享的网址是Fuun.fun&#xff0c;奇趣网站收藏家&#xff1b; 它的网址是&#xff1a;FUUN.FUN 这是一个我经常逛的网站&#xff0c;为什么我经常逛呢&#xff1f;因为可以从中发现一些有意思的网站…

Vol.46 一个在线小游戏网站,每个月50万访问量

大家好&#xff0c;我是欧维Ove&#xff0c;今天要给大家分享的网站是&#xff1a;小霸王&#xff0c;这是一个可以在线玩小霸王游戏的网站&#xff0c;网址是&#xff1a;小霸王&#xff0c;其樂無窮。紅白機&#xff0c;FC線上遊戲&#xff0c;街機遊戲&#xff0c;街機線上&…

一种驱动器的功能安全架构介绍

下图提供了驱动器实现安全功能的架构 具有如下特点&#xff1a; 1.通用基于总线或者非总线的架构。可以实现ethercat的FSOE&#xff0c;profinet的profisafe&#xff0c;或者伺服本体安全DIO现实安全功能。 2.基于1oo2D架构&#xff0c;安全等级可以达到sil3。 3.高可用性。单…

第17天:信息打点-语言框架开发组件FastJsonShiroLog4jSpringBoot等

第十七天 本课意义 1.CMS识别到后期漏洞利用和代码审计 2.开发框架识别到后期漏洞利用和代码审计 3.开发组件识别到后期漏洞利用和代码审计 一、CMS指纹识别-不出网程序识别 1.概念 CMS指纹识别一般能识别到的都是以PHP语言开发的网页为主&#xff0c;其他语言开发的网页识…

攻防世界---Web_php_include

1.题目链接 2.补充知识&#xff1a; 3.构造&#xff1a;执行成功 /?pagedata://text/plain,<?php phpinfo()?> 4.构造下面url&#xff0c;得到目录路径 /?pagedata://text/plain,<?php echo $_SERVER[DOCUMENT_ROOT]?> 5构造下面url&#xff0c;读取该路径的…

Alibaba --- 如何写好 Prompt ?

如何写好 Prompt 提示工程&#xff08;Prompt Engineering&#xff09;是一项通过优化提示词&#xff08;Prompt&#xff09;和生成策略&#xff0c;从而获得更好的模型返回结果的工程技术。总体而言&#xff0c;其实现逻辑如下&#xff1a; &#xff08;注&#xff1a;示例图…

【C++杂货铺】模板进阶

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 泛型编程 &#x1f4c1; 函数模板 &#x1f4c2; 概念 &#x1f4c2; 格式 &#x1f4c2; class 和 typename &#x1f4c2; 原理 &#x1f4c2; 函数模板实例化 &#x1f4c2; 匹配原则 &#x1f4c1; 类模板 &#x1…

全球历年GDP增长率_探数API数据统计

以下是数据的详细说明&#xff1a; 全球GDP增长最快的年份是1964年&#xff0c;全球GDP增速达到6.65%。2021年的GDP增长率也相当高&#xff0c;主要受2020年衰退后的恢复性增长推动。 全球GDP增长最慢的年份包括&#xff1a;1974年、1975年&#xff08;第一次石油危机引发&…

clion最新安装教程

还在用Dev-C吗&#xff1f;也尝试了很多C编辑器&#xff0c;不是太老&#xff0c;就是太复杂。对于c开发者来说clion真的好用&#xff0c;CLion是一款专为开发C及C所设计的跨平台IDE。难受的是cion并不免费&#xff0c;仿佛是在证明好货不贵的道理&#xff0c;只能免费用30天。…

2024年阿里云优惠券领取攻略

阿里云作为国内领先的云计算服务提供商&#xff0c;以其稳定、高效、安全的服务赢得了众多用户的青睐。为了吸引用户上云&#xff0c;阿里云经常推出各种优惠活动&#xff0c;其中就包括阿里云优惠券。本文将为大家详细解读2024年阿里云优惠券的领券攻略&#xff0c;帮助大家轻…

【WinForm】如何在自己的程序窗口中显示并调用外部桌面程序

当你爱上一个程序的功能&#xff0c;并且希望扩展它以满足自己的需求时&#xff0c;你可能会觉得困惑。毕竟&#xff0c;你已经为此付出了很多努力&#xff0c;并希望能够有效地整合这些功能。那么&#xff0c;是否可以将这些功能嵌套到自己的程序中呢&#xff1f; 首先&#…

【操作系统专题】详解操作系统 | 操作系统的目标和功能 | 操作系统如何工作

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.操作系统的目标和功能2…

计算机炸了,电子信息也是劝退专业?

还不是因为这个版本&#xff0c;计算机专业受到了制裁&#xff0c;导致这些偏计算机类的专业也受到了牵连 我本科的时候是一所双一流院校的计科专业&#xff0c;我们学校的电子信息专业堪称苦逼&#xff0c;我们计科学的东西&#xff0c;他们都要学&#xff0c;他们学的一些东…

如何使用 LangChain 构建基于LLMs的应用——入门指南

大型语言模型(LLMs)是非常强大的通用推理工具&#xff0c;在各种情况下都非常有用。 但是&#xff0c;与构建传统软件不同&#xff0c;使用LLMs存在一些挑战&#xff1a; 调用往往是长时间运行的&#xff0c;并且随着可用输出而逐步生成输出。与固定参数的结构化输入&#xf…

力扣面试150 整数转罗马数字 打表 + 贪心

Problem: 12. 整数转罗马数字 文章目录 思路&#x1f496; 打表 贪心 思路 &#x1f468;‍&#x1f3eb; 参考&#xff1a;打表贪心 &#x1f496; 打表 贪心 class Solution {public String intToRoman(int num) {int[] value {1000, 900, 500, 400, 100, 90, 50, 40, …