第四章 串

goole引擎以串为输入数据,word也是对字符串进行编辑操作的软件

4.1串的类型的定义

4.1.1 串的定义

串是由0个或多个字符组成的有限序列,串中字符的个数为串的长度,含有0个元素的串叫空串。

char str[]="abcdef";//1

对于上面的str来说,若str表示的为数组,那么这个数组的长度为7,若str表示的为字符串,则str的长度为6

串中任意元素组成的子序列称为串的子串,包含子串的串称为主串

串的逻辑结构和线性表类似,串是限定了元素为字符的线性表。从操作集上讲,串与线性表有很大的区别,线性表的操作主要针对表内的某一元素,而串的操作主要针对主串中的一个子串

4.2.2 串的存储结构

1、定长顺序存储表示

此处采用的是在字符串的末尾加上‘\0’,同时也设置一个length用于存储字符串的长度,原因是如果只是在字符串的末尾加上‘\0’的话,那么求串的长度的时候需要遍历n次,时间复杂度是O(n),但是如果有了length,那么求穿的长度的时候时间复杂度就是O(1)

1、定长顺序表示结构体为

//定长顺序存储
typedef struct{
	char str[maxSize+1];
	int length;
}SSTR;

2、变长分配存储表示结构体

在程序执行过程中可以根据需要,动态的分配结构体

//变长分配存储
typedef struct{
	char *ch;
	int length;
}DSTR;

这种方式在使用时,需要用malloc()来分配一个长度为length、类型为char的连续存储空间,分配的空间可以用函数free()释放掉,用函数malloc()分配空间如果成功的话,则返回一个指向起始地址的指针,作为串的基地址,这个地址由ch指针来指向,如果分配失败,则返回NULL

4.1.3串的基本操作

1、赋值操作(strassign())

//赋值操作
int strassign(DSTR &str,char *ch){
	if(str.ch)//如果原串有数据则释放原串
		free(str.ch);
	int len=0;
	char *c=ch;
	while(*c){//感觉*c与c[]一个性质
		++c;
		++len;
	}
	if(len==0){
		str.ch=NULL;
		str.length=0;
		return 1;
	}
	else{
		str.ch=(char*)malloc(sizeof(char)*(len+1));
		if(str.ch==NULL)//如果空间分配失败
				return 0;
		else{
			c=ch;
			for(int i=0;i<len;++i,++c)
				str.ch[i]=*c;
			str.length=len;
			return 1;
		}
	}
}

2、取串长度操作

//取串的长度的操作
int strlength(DSTR str){
	return str.length;
}

3、串比较操作

//核心操作,串比较操作,在没有比较出大小的情况下,先结束的为较小串,
//若两串同时结束则返回两串相等标记
int strcmp(DSTR str1,DSTR str2){
	for(int i=0;i<str1.length&&i<str2.length;i++)
		if(str1.ch[i]!=str2.ch[i])
			return str1.ch[i]-str2.ch[i];
	return str1.length-str2.length;
}

4、串连接操作

//串链接操作
int concat(DSTR &s,DSTR s1,DSTR s2){
	if(s.ch)//如果原串有数据存在,则释放他
		free(s.ch);
	s.ch=(char*)malloc(sizeof(char)*(s1.length+s2.length+1));
	if(s.ch==NULL)
		return 0;
	int i=0;
	for(;i<s1.length;i++)
		s.ch[i]=s1.ch[i];
	for(int j=0;j<s2.length;i++,j++)
		s.ch[i]=s2.ch[j];
	s.length=s1.length+s2.length;
	return 1;
}

5、求子串操作

//求子串的操作
int substring(DSTR &substr,DSTR str,int pos,int len){
	if(pos<0||pos>=str.length||len<0||len>str.length-pos)
		return 0;
	if(substr.ch){
		free(substr.ch);
		substr.ch=NULL;
	}
	if(len==0){
		substr.ch=NULL;
		substr.length=0;
		return 1;
	}
	else{
		substr.ch=(char*)malloc(sizeof(char)*(str.length+1));
		int i=pos;
		int j=0;
		while(i!=pos+len){
			substr.ch[j]=str.ch[i];
			++j;
			++i;
		}
		substr.ch[j]='\0';
		substr.length=len;
		return 1;
	}
}

6、串清空操作

//串清空操作
int clearstring(DSTR &str){
	if(str.ch){
		free(str.ch);
		str.ch=NULL;
	}
	str.length=0;
	return 1;
}

4.2串的模式匹配算法

4.2.1简单模式匹配

最简单的匹配方法,代码如下

//简单模式匹配算法
int index(DSTR str,DSTR substr){
	int i=0,j=0,k=i;
	while(i<str.length&&j<substr.length){
		if(str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else{
			j=0;
			i=++k;//匹配失败,i从主串的下一个位置开始,k中记录了上一次的起始位置
		}
	}
	if(j==substr.length)
		return k;
	else
		return 0;
}

4.2.2KMP算法

KMP算法简单来说就是运用一个next数组存储模式串(要匹配的串)的每一个字符前面的字符串的最长公共前后缀的长度,即要回溯到的位置,然后在与主串匹配时,如果发生不相等的冲突,则将当前的模式串下标转移到next数组的对应位置,这样可以减少重复匹配的次数,从而高效的进行匹配

下面以串的首地址为0为例展示了next数组的形式,其中next[5]=2,表示d的前面的字符串的最长公共前后缀长度为2,即为aa,此处要注意数组中的-1位置是扩展出来的,实际上是不存在的,它的存在是为了后续的匹配更加方便,它所虚拟存储的字符可以与任意字符匹配

求next的代码

void getnext(DSTR substr,int next[]){
	int i=0,j=-1;
	next[0]=-1;
	while(i<substr.length&&j<substr.length){
		if(j==-1||substr.ch[i]==substr.ch[j]){
			//j==0时就意味着可以从头开始找子串
			//substr.ch[i]==substr.ch[j]意味着此时可以继续向下匹配字符
			i++;
			j++;
			next[i]=j;
		}
		else
			//若不满足上面的条件的话,则说明出现了不同的字符,需要从上一个相同的部分重新匹配
			j=next[j];
	}
}

匹配算法

int KMP(DSTR str,DSTR substr,int next[]){
	int i=0,j=0;
	while(i<str.length&&j<substr.length){
		if(j==-1||str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else
			j=next[j];
	}
	if(j==substr.length)
		return i-substr.length;
	else
		return 0;
}

完整代码:

若串以0为首地址的话

//KMP算法
void getnext(DSTR substr,int next[]){
	int i=0,j=-1;
	next[0]=-1;
	while(i<substr.length&&j<substr.length){
		if(j==-1||substr.ch[i]==substr.ch[j]){
			//j==0时就意味着可以从头开始找子串
			//substr.ch[i]==substr.ch[j]意味着此时可以继续向下匹配字符
			i++;
			j++;
			next[i]=j;
		}
		else
			//若不满足上面的条件的话,则说明出现了不同的字符,需要从上一个相同的部分重新匹配
			j=next[j];
	}
}
int KMP(DSTR str,DSTR substr,int next[]){
	int i=0,j=0;
	while(i<str.length&&j<substr.length){
		if(j==-1||str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else
			j=next[j];
	}
	if(j==substr.length)
		return i-substr.length;
	else
		return 0;
}

若串以1为首地址的话KMP算法的形式

void getnext(DSTR substr,int next[]){
	int i=1,j=0;
	next[1]=0;
	while(i<substr.length){
		if(j==0||substr.ch[i]==substr.ch[j]){
			//j==0时就意味着可以从头开始找子串
			//substr.ch[i]==substr.ch[j]意味着此时可以继续向下匹配字符
			i++;
			j++;
			next[i]=j;
		}
		else
			//若不满足上面的条件的话,则说明出现了不同的字符,需要从上一个相同的部分重新匹配
			j=next[j];
	}
}
void KMP(DSTR str,DSTR substr,int next[]){
	int i=1,j=1;
	while(i<=str.length&&j<=substr.length){
		if(j==0||str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else
			j=next[j];
	}
	if(j>substr.length)
		return i-substr.length;
	else
		return 0;
}

4.2.3KMP算法的改进

有一种特殊的情况,比如对于字符串AAAAAB来说,它的next数组是-1123456,也就意味着,如果第五个字符不匹配的话,将会回溯5次到-1的位置为止,这样会产生浪费,所以引进nextval数组,该数组的形成规则是

1、若j=0,则赋值nextval[i]=-1

2、若j==-1 || substr[i]==substr[j],则i++,j++,再进行判断

     1)若substr[i]==substr[j]  则nextval[i]=nextval[j]

     2)  若substr[i]==substr[j]  则nextval[i]=j

3、若substr[i]!=substr[j],则j=nextval[j]

代码如下,匹配算法,同上

//KMP算法的改进
void getnextval(DSTR substr,int nextval[]){
	int i=0,j=-1;
	nextval[0]=-1;
	while(i<substr.length&&j<substr.length){
		if(j==-1||substr.ch[i]==substr.ch[j]){
			i++;
			j++;//未改进之前后面是next[i]=j;所以就相当于j就是next[i]
			//根据获得nextval的条件,如果substr.ch[i]与substr.ch[next[i]]相等的话,那么nextval[i]=nextval[j]
			//这样会减少重复判断的次数
			//如若substr.ch[i]与substr.ch[next[i]]不相等,那么意味着不会出现重复判断,所以nextval[i]=j
			if(substr.ch[i]==substr.ch[j])
				nextval[i]=nextval[j];
			else
				nextval[i]=j;
		}
		else
			j=nextval[j];
	}
}

串操作完整代码如下(可执行)

#include<stdlib.h>
#include <iostream.h>

#define maxSize 100


typedef struct{
	char *ch;
	int length;
}DSTR;
//赋值操作
int strassign(DSTR &str,char *ch){
	if(str.ch)//如果原串有数据则释放原串
		free(str.ch);
	int len=0;
	char *c=ch;
	while(*c){//感觉*c与c[]一个性质
		++c;
		++len;
	}
	if(len==0){
		str.ch=NULL;
		str.length=0;
		return 1;
	}
	else{
		str.ch=(char*)malloc(sizeof(char)*(len+1));
		if(str.ch==NULL)//如果空间分配失败
				return 0;
		else{
			c=ch;
			for(int i=0;i<len;++i,++c)
				str.ch[i]=*c;
			str.length=len;
			return 1;
		}
	}
}
//取串的长度的操作
int strlength(DSTR str){
	return str.length;
}
//核心操作,串比较操作,在没有比较出大小的情况下,先结束的为较小串,
//若两串同时结束则返回两串相等标记
int strcmp(DSTR str1,DSTR str2){
	for(int i=0;i<str1.length&&i<str2.length;i++)
		if(str1.ch[i]!=str2.ch[i])
			return str1.ch[i]-str2.ch[i];
	return str1.length-str2.length;
}
//串链接操作
int concat(DSTR &s,DSTR s1,DSTR s2){
	if(s.ch)//如果原串有数据存在,则释放他
		free(s.ch);
	s.ch=(char*)malloc(sizeof(char)*(s1.length+s2.length+1));
	if(s.ch==NULL)
		return 0;
	int i=0;
	for(;i<s1.length;i++)
		s.ch[i]=s1.ch[i];
	for(int j=0;j<s2.length;i++,j++)
		s.ch[i]=s2.ch[j];
	s.length=s1.length+s2.length;
	return 1;
}
//求子串的操作
int substring(DSTR &substr,DSTR str,int pos,int len){
	if(pos<0||pos>=str.length||len<0||len>str.length-pos)
		return 0;
	if(substr.ch){
		free(substr.ch);
		substr.ch=NULL;
	}
	if(len==0){
		substr.ch=NULL;
		substr.length=0;
		return 1;
	}
	else{
		substr.ch=(char*)malloc(sizeof(char)*(str.length+1));
		int i=pos;
		int j=0;
		while(i!=pos+len){
			substr.ch[j]=str.ch[i];
			++j;
			++i;
		}
		substr.ch[j]='\0';
		substr.length=len;
		return 1;
	}
}
//串清空操作
int clearstring(DSTR &str){
	if(str.ch){
		free(str.ch);
		str.ch=NULL;
	}
	str.length=0;
	return 1;
}
//简单模式匹配算法
int index(DSTR str,DSTR substr){
	int i=0,j=0,k=i;
	while(i<str.length&&j<substr.length){
		if(str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else{
			j=0;
			i=++k;//匹配失败,i从主串的下一个位置开始,k中记录了上一次的起始位置
		}
	}
	if(j==substr.length)
		return k;
	else
		return 0;
}
//KMP算法
void getnext(DSTR substr,int next[]){
	int i=0,j=-1;
	next[0]=-1;
	while(i<substr.length&&j<substr.length){
		if(j==-1||substr.ch[i]==substr.ch[j]){
			//j==0时就意味着可以从头开始找子串
			//substr.ch[i]==substr.ch[j]意味着此时可以继续向下匹配字符
			i++;
			j++;
			next[i]=j;
		}
		else
			//若不满足上面的条件的话,则说明出现了不同的字符,需要从上一个相同的部分重新匹配
			j=next[j];
	}
}
int KMP(DSTR str,DSTR substr,int next[]){
	int i=0,j=0;
	while(i<str.length&&j<substr.length){
		if(j==-1||str.ch[i]==substr.ch[j]){
			i++;
			j++;
		}
		else
			j=next[j];
	}
	if(j==substr.length)
		return i-substr.length;
	else
		return 0;
}
//KMP算法的改进
void getnextval(DSTR substr,int nextval[]){
	int i=0,j=-1;
	nextval[0]=-1;
	while(i<substr.length&&j<substr.length){
		if(j==-1||substr.ch[i]==substr.ch[j]){
			i++;
			j++;//未改进之前后面是next[i]=j;所以就相当于j就是next[i]
			//根据获得nextval的条件,如果substr.ch[i]与substr.ch[next[i]]相等的话,那么nextval[i]=nextval[j]
			//这样会减少重复判断的次数
			//如若substr.ch[i]与substr.ch[next[i]]不相等,那么意味着不会出现重复判断,所以nextval[i]=j
			if(substr.ch[i]==substr.ch[j])
				nextval[i]=nextval[j];
			else
				nextval[i]=j;
		}
		else
			j=nextval[j];
	}
}
void main(){
	DSTR a,b,c,d;
	char *e;
	a.ch=NULL;//此处必须加上,若不加的话被创建时不会自动成为NULL指针。在Debug模式下,VC++编译器会把未初始化的栈内存上的指针全部填成 0xcccccccc ,当字符串看就是 “烫烫烫烫……”;
	b.ch=NULL;
	c.ch=NULL;
	d.ch=NULL;
	//初始化串
	strassign(a,"abcdef");
	strassign(b,"abcdef");
	e=a.ch;
	for(int i=0;i<a.length;i++,e++)
		cout<<*e;
	cout<<endl;
	e=b.ch;
	for(i=0;i<b.length;i++,e++)
		cout<<*e;
	cout<<endl;
	//计算串的长度
	cout<<"计算串的长度"<<strlength(a)<<endl;
	//对比串
	cout<<"对比串"<<strcmp(a,b)<<endl;
	//连接串
	concat(c,a,b);
	cout<<"连接串"<<endl;
	e=c.ch;
	for(i=0;i<c.length;i++,e++)
		cout<<*e;
	cout<<endl;
	//求子串
	substring(d,a,1,2);
	cout<<"求子串"<<endl;
	for(i=0;i<d.length;i++)
		cout<<d.ch[i];
	cout<<endl;
	//求子串的位置
	strassign(a,"abcdef");
	strassign(b,"cde");
	cout<<"求子串的位置"<<index(a,b)<<endl;
	//KMP
	int next[10];
	strassign(a,"abababb");
	strassign(b,"adcababababb");
	getnext(a,next);
	for(i=0;i<a.length;i++)
		cout<<next[i]<<" ";
	cout<<endl;
	cout<<"KMP:"<<KMP(b,a,next)<<endl;
	//KMP改进
	int nextval[10];
	strassign(a,"abababb");
	strassign(b,"adcababababb");
	getnextval(a,nextval);
	for(i=0;i<a.length;i++)
		cout<<nextval[i]<<" ";
	cout<<endl;
	cout<<"KMP:"<<KMP(b,a,nextval)<<endl;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值