[数据结构]第四章--串(读书笔记2)

本文深入探讨了串的堆分配储存表示、串的生成与复制、空串判断、串长度获取、串比较、串清空、串连接、子串获取、删除子串、插入子串、替换子串、销毁串、打印串等功能,并详细介绍了串的模式匹配算法,包括基本算法和改进的KMP算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第四章-串

□4.2 串的表示和实现

// PROJECT04-02.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "c1.h"
/*----------------------------------------
串的堆分配储存表示
------------------------------------------*/
typedef struct{
	char* ch;		/*若是非空串,则按串长分配存储区,否则ch为NULL*/
	int length;		/*串长度*/
}HString;

Status StrAssign(HString *T,char *chars)
{ /* 生成一个其值等于串常量chars的串T */
	char *c = NULL;
	int i = 0;
	int count = 0;
	if (T->ch){
		free(T->ch);/*释放T原有的空间*/
	}
	count = strlen(chars); /* 求chars的长度i */
	if (count == 0){
		T->ch = NULL;
		T->length = 0;
	}
	else{
		if (!(T->ch = (char*)malloc(count + sizeof(char)))){
			exit(OVERFLOW);
		}
		T->length = count;
	}
	for(i = 0; i < T->length; i++){
		*(T->ch + i) = *(chars + i);
	}
	return OK;
}
Status StrCopy(HString *T,HString S)
{ /* 初始条件:串S存在。操作结果: 由串S复制得串T */
	int i;
	if((*T).ch){
		free((*T).ch); /* 释放T原有空间 */
	}
	(*T).ch = (char*)malloc(S.length * sizeof(char)); /* 分配串空间 */
	(*T).length = S.length;
	for(i=0; i < S.length; i++){
		*(T->ch + i) = *(S.ch + i);
	}
	return OK;
}
Status StrEmpty(HString S)
{ /* 初始条件: 串S存在。操作结果: 若S为空串,则返回TRUE,否则返回FALSE */
	if(S.length == 0 && S.ch == NULL){
		return TRUE;
	}
	else{
		return FALSE;
	}

}
int StrLength(HString S)
{
	return S.length;
}
int StrCompare(HString S,HString T)
{ /* 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0 */
	int i;
	for (i = 0;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;	
}
Status ClearString(HString *S)
{	/*将S清为空串*/
	if ((*S).ch){
		free((*S).ch);
		(*S).ch = NULL;
	}
	(*S).length = 0;
	return OK;
}
Status Concat(HString *T, HString S1, HString S2)
{	/*用T返回由S1和S2联结而成的新串*/
	int i;
	if((*T).ch){
		free((*T).ch); /* 释放T原有空间 */
	}
	if(!((*T).ch = (char*)malloc((S1.length + S2.length) * sizeof(char))) ){
		exit(OVERFlOW);
	}
	(*T).length = S1.length + S2.length;
	for(i = 0;i < S1.length; i++ ){
		(*T).ch[i] = S1.ch[i];
	}
	for(i = 0;i < S2.length; i++ ){
		(*T).ch[i + S1.length] = S2.ch[i];
	}
	return OK;
}
Status SubString(HString *Sub, HString S,int pos,int len)
{	/* 用Sub返回串S的第pos个字符起长度为len的子串。 */
	/* 其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1 */
	int i;
	if (pos < 1 || pos > S.length || len < 0 || len > S.length - pos + 1 ){
		return ERROR;
	}
	if (Sub->ch){	/* 释放旧空间 */
		free(Sub->ch);
		Sub->ch = NULL;
	}
	if(len == 0){		/* 空子串 */
		Sub->ch = NULL;
		Sub->length = 0;
	}
	else{			/* 完整子串 */
		Sub->ch = (char*)malloc(len * sizeof(char));
		for (i = 0; i < len; i++){
			Sub->ch[i] = S.ch[pos + i - 1];
		}
		Sub->length = len;
	}
	return OK;
}
void InitString(HString *T)
{ /* 初始化(产生空串)字符串T。另加 */
	(*T).length = 0;
	(*T).ch = NULL;
}
int Index(HString S, HString T, int pos) /* 算法4.1 */
{	/* T为非空串。若主串S中第pos个字符之后存在与T相等的子串, */
	/* 则返回第一个这样的子串在S中的位置,否则返回0 */
	int n, m, i;
	HString sub;
	InitString(&sub);
	if(pos > 0){
		n = StrLength(S);
		m = StrLength(T);
		i = pos;
		while(i <= n - m + 1){
			SubString(&sub, S, i, m);
			if(StrCompare(sub, T) != 0){
				++i;
			}
			else{
				return i;
			}
		}
	}
	return 0;
}
Status StrDelete(HString *S,int pos,int len)
{ /* 从串S中删除第pos个字符起长度为len的子串 */
	int i;
	if((*S).length < pos + len -1){
		exit(ERROR);	
	}
	for(i = pos-1; i <= (*S).length - len; i++){
		(*S).ch[i] = (*S).ch[i + len];
	}
	(*S).length -= len;
	(*S).ch = (char*)realloc((*S).ch,(*S).length * sizeof(char));
	return OK;
}
Status StrInsert(HString *S,int pos,HString T) /* 算法4.4 */
{ /* 1≤pos≤StrLength(S)+1。在串S的第pos个字符之前插入串T */
	int i;
	if (pos >= 1 && pos >= StrLength(*S)+1){
		exit(ERROR);
	}
	(*S).ch = (char*)realloc((*S).ch,((*S).length + T.length) * sizeof(char));
	if(!(*S).ch){
		exit(OVERFLOW);
	}
	/* 为插入T而腾出位置 */
	for(i = (*S).length - 1 ;i >= pos-1; i--){
		(*S).ch[i+T.length] = (*S).ch[i];
	}
	for(i=0; i< T.length; i++){
		(*S).ch[i+pos-1] = T.ch[i];/* 插入T */
	}
	(*S).length += T.length;
	return OK;
}
Status Replace(HString *S,HString T,HString V)
{	/* 初始条件: 串S,T和V存在,T是非空串(此函数与串的存储结构无关) */
	/* 操作结果: 用V替换主串S中出现的所有与T相等的不重叠的子串 */
	int i=1; /* 从串S的第一个字符起查找串T */
	if(StrEmpty(T)){ /* T是空串 */
		return ERROR;
	}
	do{
		i = Index(*S,T,i);
		if(i){ /* 串S中存在串T */
			StrDelete(S,i,T.length);	/* 删除该串T */
			StrInsert(S,i,V);		 /* 在原串T的位置插入串V */
			i += StrLength(V); /* 在插入的串V后面继续查找串T */
		}
	}while(i);
	return OK;
}
void DestroyString()
{ /* 堆分配类型的字符串无法销毁 */
}
void StrPrint(HString T)
{ /* 输出T字符串。另加 */
	int i;
	for(i = 0; i < T.length; i++){
		printf("%c", T.ch[i]);
	}
	printf("\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
	int i;
	char c,*p="God bye!",*q="God luck!";
	HString t,s,r;
	InitString(&t); /* HString类型必需初始化 */
	InitString(&s);
	InitString(&r);
	StrAssign(&t,p);
	printf("串t为: ");
	StrPrint(t);
	printf("串长为%d 串空否?%d(1:空 0:否)\n",StrLength(t),StrEmpty(t));
	StrAssign(&s,q);
	printf("串s为: ");
	StrPrint(s);
	i=StrCompare(s,t);
	if(i<0)
		c='<';
	else if(i==0)
		c='=';
	else
		c='>';
	printf("串s%c串t\n",c);
	Concat(&r,t,s);
	printf("串t联接串s产生的串r为: ");
	StrPrint(r);
	StrAssign(&s,"oo");
	printf("串s为: ");
	StrPrint(s);
	StrAssign(&t,"o");
	printf("串t为: ");
	StrPrint(t);
	Replace(&r,t,s);
	printf("把串r中和串t相同的子串用串s代替后,串r为:\n");
	StrPrint(r);
	ClearString(&s);
	printf("串s清空后,串长为%d 空否?%d(1:空 0:否)\n",StrLength(s),StrEmpty(s));
	SubString(&s,r,6,4);
	printf("串s为从串r的第6个字符起的4个字符,长度为%d 串s为: ",s.length);
	StrPrint(s);
	StrCopy(&t,r);
	printf("复制串t为串r,串t为: ");
	StrPrint(t);
	StrInsert(&t,6,s);
	printf("在串t的第6个字符前插入串s后,串t为: ");
	StrPrint(t);
	StrDelete(&t,1,5);
	printf("从串t的第1个字符起删除5个字符后,串t为: ");
	StrPrint(t);
	printf("%d是从串t的第1个字符起,和串s相同的第1个子串的位置\n",Index(t,s,1));
	printf("%d是从串t的第2个字符起,和串s相同的第1个子串的位置\n",Index(t,s,2));
	return 0;
}

□4.3 串的模式匹配算法
求子串位置的定位函数Index(S,T,pos)
子串的定位操作通常称作串的模式匹配,是各种串处理系统中最重要的操作之一。在Index这个函数中,分别利用计数指针i和j指示主串S和模式串T中当前正待比较的字符位置。算法的基本思想是:从主串S的第pos各字符起和模式的第一个字符比较之,若相等则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式的字符比较之。

□4.3.2 模式匹配的一种改进算法
克努特-莫里斯-普拉特操作(KMP算法)
其改进在于每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的"部分匹配"的结果将模式向右"滑动"尽可能远的一段距离后,继续进行比较。
KMP算法仅当模式与主串之间存在许多"部分匹配"的情况下才显得比算法4.5快得多。但是KMP算法的最大特点就是指示主串的指针不需回溯。
next[j]表明当模式中第j个字符与主串中相应字符失配时,在模式中需要重新和主串中该字符进行比较的字符的位置。匹配过程如下:假设以指针i和j分别指示主串和模式中正待比较的字符,令i的初值为pos,j的初值为1。若在匹配过程中si = pj,则i和j分别增1,否则i不变,而j退到next[j]的位置再比较,若相等,则指针各自增1,否则j再退到下一个next值的位置,依次类推,直至下列两种可能:一种是j退到某个next值时比较相等,则指针各自增1,继续进行匹配;另一种是j退到值为0,则此时需将模式继续向右滑动一个位置。即从主串的下一个字符Si+1起和模式重新开始匹配。虽然该算法的时间复杂度是O(n*m),但在一般情况下,其实际的执行时间近似于O(n+m),因此至今仍被采用。KMP算法仅当模式与主串之间存在许多"部分匹配"的情况下才显得快得多。KMP算法的最大特点是指示主串的指针不需回溯,整个匹配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边读入边匹配,而无需回头重读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值