【数据结构】串(String)

news2025/1/19 19:19:30

文章目录

  • 基本概念
  • 顺序存储结构
    • 比较当前串与串s的大小
    • 取子串
    • 插入
    • 删除
    • 其他
      • 构造函数
      • 拷贝构造函数
      • 扩大数组空间。
      • 重载+
      • 重载=
      • 重载==
      • 重载[]
      • 重载>>
      • 重载<<
  • 链式存储结构
    • 链式存储结构
    • 链块存储结构
  • 模式匹配
    • 朴素的模式匹配算法(BF算法)
    • KMP算法
      • 字符串的前缀、后缀和部分匹配值
      • next数组
      • nextVal数组
      • KMP代码实现
  • 扩展与提高
  • 参考资料

基本概念

从数据结构角度讲,串属于线性结构。
特点:(1)串中的一个元素是一个字符 (2)操作对象是一组元素

字符串和普通数组的区别?
串的元素是字符,要在最后一个字符的后面多存放’\0’,作为结束符

串的长度:一个串所包含的字符的个数n
空串:串的长度=0。串内没有任何字符。
空格串:有空格字符组成的串。
子串:串中任意个连续字符组成的子序列
主串:包含子串的串
模式匹配(子串定位):查找子串在主串中第一次出现的位置

class String
public:
	String(const char *str = NULL);		//构造函数
	String(const String &str);			//拷贝构造函数
	~String(){ delete []data;}			//析构函数
	
	int capacity( const { return maxSize;}	//最大存储容量
	int size()const {return curLength;} 	//求串长度
	bool empty() const {return curLength==0;}	//判空
	
	int compare(const String &s) const;			//比较当前串和串s的大小
	String subStr(int pos, int num)const;	//从pos位置开始取长度为num的子串
	String &insert(int pos,const String &s);	//在pos位置插入串S
	String &erase(int pos, int num);			//删除从 pos开始的 num个字符
	const char* toCharStr() const{ return data;}	// 获取字符数组 data
	
	int bfFind(const String &s, int pos = 0) const;	// 朴素的模式匹配算法	
	int kmpFind(const String &t, int pos= 0);	// 改进的模式匹配算法
	void getNext(const String &t, int *next)//获取 next数组
	void getNextVal (const String&t, int *nextVal)// 获取 nextVal 数组
	
	bool operator==(const String &str)const; 	//重载==,判断两个串是否相等
	String& operator+(const String &str);	//重载+,用于串的连接
	String& operator=(const String &str); 	//重载=,用于串间赋值
	char& operator[] (int n) const;	//重载[],通过下标运算取串中字符
	friend istream& operator>>(istream &cin, String &str);//重载>>,用于输入串
	friend ostream& operator<<(ostream &cout, String &str)// 重载<<,用于输出串

private:
	char *data;		//存储串
	int maxSize;	//最大存储容量
	int curLength;	//串的长度
 	void resize(int len);	//扩大数组空间
}:

class outOfRange:public exception {		//用于检查范围的有效性
public: 
	const char* what()const throw(){
		return "ERROR!OUT OF RANGE. \n";
	}
};
	
class badSize:public exception {		//用于检查长度的有效性
public:
	const char* what()const throw(){
		return "ERROR! BAD SIZE.\n";
	}
};

顺序存储结构

比较当前串与串s的大小

两个串相等,返回0; 当前串大,返回1;当前串小,返回-1。

int String::compare(const String &s) const{
	int i = 0;
	while(s.data[i] != '\0' || this->data[i] !='\0'){
		ifthis->data[i] > s.data[i])
			return 1;
		else if (this->data[i] < s.data[i])
			return -1:
		i++;
	}
		
	if (this->data[i] =='\0' && s.data[i] !='\0')	//s有剩余元素
		return -1;
	else if (s.data[i] =='\0' && this->data[i] !='\0')
			return 1;				//this有剩余元素
		return 0;							//均无剩余元素
}

上述代码中的函数体可替换成return strcmp( data , s.toCharStr() );语句,即调用头文件中的库函数strcmp 实现串比较。

取子串

从主串中下标为pos的位置开始取长度为num的子串
当poscurLength或num0时,取到的子串为空串
当 num > curLength - pos时,修改 num =curLengh-pos

String String :: subStr(int pos, int num)const{
	int i;
	String tmp("");
	if (pos > curLength || pos < 0 )
		throw outOfRange():
	if (num < 0) throw badSize();	// num<O,抛出异常
	if (num > curLength - pos)		//num大于从pos开始到串尾的元素个数
		num = curLength - pos;		// 修改 num的值
		
	delete []tmp.data;				//释放 tmp原来的存储空间
	tmp.maxSize = tmp.curLength = num;
	tmp.data = new char[num+1];		//申请大小为num+1的存储空间
	for (i=0; i<num; i++){		//长度为num的子串赋值给tmp
		tmp.data[i]=data[pos+i];
	tmp.data[i] ='\0';
	return tmp;
}

插入

在当前串的pos位置插入串s。

String & String::insert(int pos, const String &s){
	if (pos > curLength || pos < 0)	// pos 合法范围是[O..curLength]
		throw outOfRange();		//抛出异常
	if(curLength+s.curLength > maxSize)		//空间不够
		resize(2*(curLength+s.curLength));	//扩大数组空间
		
	for (int i = curLength; i >= pos: i--)	//下标在[curLength..pos]范围内的元素
		data[i+s.curLength]=data[i];		//往后移动s.curLength,包括`\0`
	for (int j = 0; j < s.curlength; j++) 	//存储s中的字符
		data[pos+j] = s.data[j];
	curLength += s.curLength;	//修改串的长度
	return *this;
}

删除

在当前串中删除从pos位置开始的长度为num的子串

String & String::erase(int pos, int num){
	if (pos < 0 || pos > curLength-1)	//合法的删除位置为[0..curLength-1]
		throw outOfRange();
	if(num < 0) throw badSize();		//num<0 抛出异常
	if (num > curLength - pos)		//num大于从pos开始到串尾元素个数
		num = curLength-pos;			//修改 num的值
		
	for (int j = pos+num; j<=curLength; j++)//下标在[posfnum curLength]内的元素
		data[j-num]= data[j];		//向前移动num,包括'\0'
	curLength -= num;				//修改串的长度
	return *this;

}

其他

构造函数

String::String(const char *str) {
	maxSize = 2*strlen(str);
	data = new char [maxSize + 1];
	strcpy_s(data, maxSize+1, str);
	curLength = strlen(data);
	}

strcpy_s是一个字符串复制函数,它接受一个目标数组、目标数组的大小和一个源字符串作为参数,并防止缓冲区溢出。
strlen() 用于计算一个以空字符(即 ‘\0’)结尾的字符串的长度。这个函数返回的是字符串中字符的个数,不包括终止的空字符

拷贝构造函数

String::String(const String &str){
	maxSize = str. maxSize;
	data = new char[maxSize + 1];
	strcpy_s(data, maxSize+1, str.toCharStr());
	curLength =str.curLength;
	}

toCharStr()是成员函数

扩大数组空间。

void String::resize(int len){
	maxSize = len;	//修改数组的最大容量为1en
	char *temp = new char[maxSize + 1]; 
	strcpy_s(temp, maxSize+1, data);
	delete []data;
	data=temp;
}

重载+

用于串的连接。

String& String::operator+(const String &str){
	if( maxSize < curLength + str.size());	//加上str后,数组空间不够了
		resize( 2*(curLength+str.size()) );
	strcat_s(data, maxSize+1, str.data);
	curlength += str.curlength;
	return *this;
}

strcat_s是一个字符串拼接函数,用于将源字符串追加到目标字符串的末尾。strcat_s(目标字符的指针, 字符串缓冲区的大小, 源字符的指针);

重载=

用于串赋值。

String& String::operator=(const String &str){
	if(this == &str) 
		return *this;	//当前串和str是同一个串
		
	delete []data;		//释放原空间
	maxSize = str.maxSize;
	data = new char[maxSize + 1];	//重新申请一块新的存储空间
	strcpy_s(data, maxSize+1,str.toCharStr());
	curLength =str.curLength;
	return *this;
}

串的赋值运算会改变原有的串值,为了避免内存泄漏,最好释放原空间后再重新中新的存储空间。

重载==

用于判断两个串是否相等。

bool String::operator==(const String &str) const{
	if (curLength != str.curLength) 
		return false;
		
	return strcmp(data,str.data) ? false : true;	//调用cstring库的strcmp函数
}

重载[]

用于通过下标运算存取串中字符。

inline char& String::operator[](int n) const{
	if(n<0 || n>= curLength) 
		throw outOfRange();		//下标越界时抛出异常
		
	else return data[n];
}

重载>>

用于输入串。

istream& operator>>(istream &cin, String &str){
	char *temp=new char[10000];		//申请临时空间
	cin >> temp;	
	str.maxSize = 2*strlen(temp);
	str.data = new char[str.maxSize +1];
	strcpy _s(str.data, str.maxSize+1, temp);
	str.curLength=strlen(temp);
	delete []temp;
	return cin;
}

重载<<

用于输出串。

ostream& operator<<(ostream &cout, String &str){
	cout << str.data;
	return cout;
}

链式存储结构

链式存储结构

因为一个元素即为一个字符,最简单的结构是再一个结点的数据域中存放一个字符。
但存储密度较低,存储密度=串值所占的存储单元数 / 实际分配的存储单元数
在这里插入图片描述

链块存储结构

一个结点的数据与中存放多个字符。
但插入和删除操作会在字符间大量的移动字符。
在这里插入图片描述

综上,一般串采用顺序存储结构来表现和实现

模式匹配

设有主串S和子串T,如果在主串S中找到一个与子串T相等的子串,则返回子串T第一次在主串S中出现的位置,即子串T的第1个字符 在主串S中的位置。
主串又称为目标串,子串又称为模式串

朴素的模式匹配算法(BF算法)

从主串S=“S0S1…Sn-1”的第一个字符开始与子串T=“T0T1…Tm-1”中的第一个字符进行比较。
若Si与Tj相等,则指示两个串的指针 i 和 j 后移,继续下一个字符的比较;
若Si与Tj不相等,则主串指针i回溯到上一次比较的起始位置的后一位(即i=i-j+1),而子串指针j回溯到第一个字符(即j=0),继续下一个字符的比较。
若匹配成功,则返回子串的第一个字符在主串的位置;否则,返回-1.

时间复杂度:O(mn)*

在这里插入图片描述
在这里插入图片描述

int String::bfFind(const String &s, int pos) const{
	int i=0, j=0;
	if(curLength < s.curLength)
		return -1;
	
	while(i<curLength && j < s.curLength){
		if(data[i]==s.data[j])
			i++,j++;
		else{
			i=i-j+1;
			j=0;
		}
	}
	if(j>=s.curLength)
		return (i-s.curLength);
	else
		return -1;
}

KMP算法

特点:主串无需回溯,主串指针一直往后移动,只有子串指针回溯。
时间复杂度:O(m+n)

在这里插入图片描述
如上图所示,此时KMP算法的关键问题变成了如何求K的值。为了计算每次失配时子串指针j回溯的位置k,KMP采用空间换时间的方式,申请一个与子串长度相同的整型数组next。

字符串的前缀、后缀和部分匹配值

要了解子串的结构,首先要弄清楚几个概念:前缀、后缀和部分匹配值。
前缀:除最后一个字符以外,字符串的所有头部子串
后缀指除第一个字符外,字符串的所有尾部子串
部分匹配值则为字符串的前缀和后缀的最长相等前后缀长度

下面以’ababa '为例进行说明:

前缀后缀最长相等前后缀的长度
‘a’空集空集0
‘ab’{a}{b}0
‘aba’{a,ab}{a,ba}1,即{a}
‘abab’{a,ab,aba}{b,ab,bab}2,即{ab}
‘ababa’{a,ab,aba,abab}{a,ba,aba,baba}3,即{aba}

所以字符串’ababa’的部分匹配值为00123。

next数组

表示在匹配失败后子串可跳过的字符的总数。
next数组的求解方法
首两位是0,1.后面的规律是:数值=n+1 (n为最长相等前后缀的长度)

下面以’abaabc '为例进行说明:

序号123456
模式串abaabc
next数组011223

有些版本是以-1,0开头,但计算思路都一样。相当于以0,1开头的数组元素全部减1

代码实现:

void String::getNext(const String &t, int *next){
	int i = 0, j = -1;
	next[0] = -1;
	while(i < t.curLength-1){
		if((j==-1 || (t[i]==t[j])){
			++i,++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
}

以主串S=“GoogleGooseGood”,子串="Good"为例,匹配过程如下:
在这里插入图片描述

nextVal数组

next数组的进一步优化。

nextVal数组的求解方法
首位是0,剩下的根据next的找到对应序列的字母
若相等:根据next值找到对应的序列,=其序列的nextVal
若不同:nextVal=next

下面以’abaabc '为例进行说明:

序号123456
模式串abaabc
next数组011223
字母aabba
nextVal数组010213

1.先用next的值去对应序列的值,把字母填上。
2.若字母不等于模式串,如序列2,4,6,则nextVal=next
3.若字母等于模式串,根据next的数值找到相同的序号值,再找到这个序号的nextVal值,这个值即是要寻找的数值

以主串S=“aaabaaabaaaab”,子串="aaab"为例,匹配过程如下:
在这里插入图片描述

若使用next数组,第二次匹配时,i=3,j=0

void String::getNextVal(const String&t, int *nextVal){
	int i=0,j=-1;
	nextVal[0]=-1;
	while(i<t.curLength){
		if((j==-1 || (t[i]==t[j])){
			++i,++j;
			if(t[i] !=t[j])
				nextVal[i]=j;
			else
				nextVal[i] = nextVal[j];
			}
			else j = nextVal[j];
	}
}

代码说明:getNextVal函数在getNext函数的基础上增加了对子串中字符t[i]和t[j]是否相等的判断,若t[i]==t[j],则将nextVal[j]的值传递给nextVal[i].否则,与getNext函数一样,nextVal[i]=j

KMP代码实现

int String::kmpFind(const String &t, int pos){
	if(t.curLength == 0)
		return 0;
	if(curLength < t.curLength)
		return -1;
	int i=0,j=0;
	int *next=new int[t.curLength];
	getNext(t,next);
	while(i<curLength && j < t.curLength){
		if(i<curLength && j<t.curLength)
			i++,j++;
		else 
			j=next[j];
	}	
	delete []next;
	if(j>=t.curLength)
		return (i-t.curLength);
	else
		return -1;
}

扩展与提高

在C++中,C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。

编程时加入头文件:

#include<string>	//或者万能头文件#include<bits/stdc++.h>
using namespace std;

String函数:
用string定义s类

定义什么都可以,只要把s变成定义的字母就可以调用C++中的函数

具体使用方法为:

常见操作
s.resize(10)设置字符串长度为10
string s(“ABC”)构造字符串s的值为ABC
s.empty()判断字符串是否为空
s.length()或者s.size()求解字符串是否为空
s.begin()开始值
s.end()结尾值
查找(查找成功返回元素位置,失败返回-1)
s.find(‘A’)查找字符A
s.find(“ABC”)查找字符串ABC
s.find(‘A’,2)从位置2开始查找字符A
s.find(“ABCD”,1,2)从位置1开始,查找ABCD的前两个字符
s.rfind()从字符串尾部开始查找
插入
s.insert(2,3,‘A’)在下标为2的地方添加三个A
s.insert(2,“ABC”)在下标为2的地方添加字符串ABC
s.insert(2,“ABC”,1)在下标为2的地方添加ABC中的一个
s.insert(2,“ABCD”, 2,2)在下标为2的地方从字符串ABCD中位置2开始的2个字符
s.push_back(‘A’)在尾部添加字符A
复制
s1=s.substring(2)提取字符串s从2到尾部赋值给s1
s1=s.substring(2,3)提取字符串s从2开始三个字符赋值给s1
const char*s1=s.data()将string类转为字符串数组
s.copy(s1,2,3)将s中从第3个位置拷贝2个字符到s1中
比较(假设原字符串为ABCD)
s.compare(“ABCD”)相等返回0,大于原字符串返回1,小于返回-1
删除
s.erase(2)删除下标2以后的全部元素
s.erase(2,1))删除下标2以后的第一个元素
翻转
reverse(s.begin(),s.end())翻转所有字符串,即逆序输出
清空
s.assign(“ABC”)清空字符串,并置为ABC
s.assign(“ABC”,2)清空字符串,并置为ABC的前2个字符AB
s.assign(“ABC”,2,1)清空字符串,并置为ABC的从2开始的1个字符
s.assign(5,‘A’)清空字符串,并置为5个A
s.clear()清空字符串所有字符

参考资料

1.【图解数据结构】串全面总结
2.串的详细讲解

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

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

相关文章

RuoYi-Vue-Plus (角色部门-数据权限 @DataPermission使用、自定义数据权限、数据权限拦截 、处理器解读)

本章主要是数据权限控制: 本章按照对部门id,用户id举例控制数据权限 功能: 1.支持自动注入 sql 数据过滤 2.查询、更新、删除 限制 3.支持自定义数据字段过滤 4.模板支持 spel 语法实现动态 Bean 处理 一、角色部门权限@DataPermission 1-使用及简介 权限范围五个 …

《环阳宗海逍遥游》

第一天:《六十八道拐》五月二日游兴浓&#xff0c;大观公园门囗逢。海埂西门再集合&#xff0c;蓝光城里意无穷。呈贡过后松茂过&#xff0c;阳宗镇上心欢融。宜良城中暂歇脚&#xff0c;六十八拐路难通。宜良住宿赏夜色&#xff0c;期待明朝再接龙。 第二天:《情人岛苗王峡行》…

用C#写一个读取pdf文档内容的库

安装这两个库&#xff0c;第二个库一定要安装否则有些pdf文件读取会出现异常 读取 using iText.Kernel.Pdf; using iText.Kernel.Pdf.Canvas.Parser; using iText.Kernel.Pdf.Canvas.Parser.Listener;namespace TestReadPdf {public static class PdfHelper{public static IE…

布局香港之零售小店篇 | 香港一人小企与连锁超市的竞争

近年来&#xff0c;内地品牌入驻香港市场开拓业务已成大势所趋。香港特区政府早前公布的「2023年有香港境外母公司的驻港公司按年统计调查」显示&#xff0c;2023年母公司在海外及内地的驻港公司数量高达9039家。内地品牌在香港的成功落地&#xff0c;不仅为香港市民带来了丰富…

「布道师系列文章」小红书黄章衡:AutoMQ Serverless 基石-秒级分区迁移

作者&#xff5c;黄章衡&#xff0c;小红书消息引擎研发专家 01 引言 Apache Kafka 因存算一体化架构&#xff0c;分区迁移依赖大量数据同步的完成&#xff0c;以一个 100MB/s 流量的 Kafka 分区为例&#xff0c;运行一天产生的数据量约为 8.2T&#xff0c;如果此时需要将该分…

【数据结构(邓俊辉)学习笔记】向量04——有序向量

文章目录 0.概述1.比较器2.有序性甄别3.唯一化3.1低效算法3.1.1实现3.1.2 复杂度3.1.3 改进思路3.2 高效算法3.2.1 实现3.2.2 复杂度 4.查找4.1统一接口4.2 语义定义4.3 二分查找4.3.1 原理4.3.2 实现4.3.3 复杂度4.3.4 查找长度4.3.5 不足 4.4 Fibonacci查找4.4.1 改进思路4.4…

AI-数学-高中-42导数的概念与意义

原作者视频&#xff1a;【导数】【一数辞典】1导数的概念与意义_哔哩哔哩_bilibili .a是加速度&#xff1b;

【Spring篇 | 补充】三级缓存解决循环依赖

文章目录 7.三级缓存解决循环依赖7.1何为循环依赖&#xff1f;7.2三级缓存解析7.3三级缓存解决循环依赖7.3.1实例化A7.3.2创建B的需求7.3.3实例化B7.3.4注入A到B7.3.5B创建完成7.3.6回溯至A7.3.7清理二级缓存 7.4为什么不能用二级缓存解决循环依赖&#xff1f; 7.三级缓存解决循…

【漏洞复现】通天星CMSV6车载监控平台ids SQL注入漏洞

漏洞描述&#xff1a; 通天星CMSV6车载定位监控平台拥有以位置服务、无线3G/4G视频传输、云存储服务为核心的研发团队&#xff0c;专注于为定位、无线视频终端产品提供平台服务&#xff0c;通天星CMSV6产品覆盖车载录像机、单兵录像机、网络监控摄像机、行驶记录仪等产品的视频…

微信小程序4~6章总结

目录 第四章 页面组件总结 4.1 组件的定义及属性 4.2 容器视图组件 4.2.1 view 4.2.2 scroll-view 4.2.3 swiper 4.3 基础内容组件 4.3.1 icon ​编辑 4.3.2 text 4.3.3 progress ​编辑 4.4 表单组件 4.4.1 button 4.4.2 radio 4.4.3 checkbox 4.4.4 switch …

网工学习云计算HCIE感受如何?

作为一名网工&#xff0c;我经常会在各种网络论坛里查询搜索一些网络技术资料&#xff0c;以及跟论坛里的网友交流讨论平时在工作、学习中遇到的问题、故障&#xff0c;因此也经常能在论坛的首页看到誉天的宣传信息。机缘巧合之下关注了誉天的B站号&#xff0c;自从关注了誉天的…

用 LMDeploy 高效部署 Llama-3-8B,1.8倍vLLM推理效率

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总…

嵌入式Linux driver开发实操(十八):Linux音频ALSA开发

应用程序程序员应该使用库API,而不是内核API。alsa库提供了内核API 100%的功能,但增加了可用性方面的主要改进,使应用程序代码更简单、更美观。未来的修复程序或兼容性代码可能会放在库代码中,而不是放在内核驱动程序中。 使用ALSA API和libasound进行简单的声音播放: /*…

Gartner发布攻击面管理创新洞察:CTEM、VA、EASM、CAASM、ASA、DRPS、BAS、VM等攻击面管理相关技术及关系

安全运营团队负责管理跨内部和外部数字资产的复杂攻击面。这项研究概述了攻击面评估空间&#xff0c;以帮助安全和风险管理领导者驾驭技术并改善其安全状况。 主要发现 随着本地和云中的技术环境变得越来越复杂和分散&#xff0c;组织必须管理不断增长的攻击面。 SaaS 应用程序…

wps/word中字体安装教程

问题&#xff1a;下载的字体怎么导入wps/word wps或word中没有相应字体&#xff0c;怎么导入。其实方法很简单。 Step 1&#xff1a;下载字体 首先&#xff0c;在网上搜索自己喜欢的字体&#xff0c;然后下载到本地。字体的格式通常是.ttf 下面是我网上找的字体&#xff08…

2024年度西安市创新联合体备案申报条件时间要求须知

一、申报条件 组建市级创新联合体需具备牵头单位、成员单位、组建协议、首席科学家等四个条件。 (一)牵头单位 1.牵头单位应为在西安市注册登记的省市产业链龙头骨干企业&#xff0c;重点支持市级重点产业链“链主”企业; 2.牵头单位一般为1家。 (二)成员单位 1.成员单位…

2024最新版JavaScript逆向爬虫教程-------基础篇之JavaScript密码学以及CryptoJS各种常用算法的实现

目录 一、密码学介绍1.1 为什么要学密码学?1.2 密码学里面学哪一些 二、字符编码三、位运算四、Hex 编码与 Base64 编码4.1 Hex 编码4.2 Base64 编码 五、消息摘要算法5.1 简介5.2 JS中的MD5、SHA、HMAC、SM3 六、对称加密算法6.1 介绍6.2 加密模式和填充方式6.3 CryptoJS 中D…

代理IP干货:如何正确使用防范风险?

在今天的数字时代&#xff0c;代理IP地址已成为互联网世界中不可或缺的一部分。无论您是寻求绕过地理限制、保护个人隐私还是执行网络任务&#xff0c;代理IP地址都发挥着关键作用。我们将为您探讨代理IP地址的重要性以及如何防范潜在的风险和威胁。 一、代理IP地址的潜在风险 …

CUDA编程技术概述

CUDA&#xff08;Compute Unified Device Architecture&#xff0c;统一计算设备架构&#xff09;是由英伟达&#xff08;NVIDIA&#xff09;公司推出的一种软硬件集成技术&#xff0c;是该公司对于GPGPU&#xff08;通用图形处理器计算&#xff09;的正式名称。透过这个技术&a…

微信小程序用户隐私协议保护指引自定义组件封装

这是一个微信小程序用户隐私协议保护指引自定义组件封装详细教程及代码。【建议收藏】 在做微信小程序有涉及表单提交&#xff0c;涉及用户信息收集时。提交代码会审核不过。 有需要了解到文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/framework/user-pr…