数据结构与算法:线性表之串

本文详细介绍了串(字符串)的概念,包括定义、子串和与线性表的区别。接着,讨论了串的三种基本存储方式:定长顺序存储、堆分配存储和块链存储,以及相应的操作如连接、子串提取和赋值。最后,重点阐述了两种模式匹配算法:BF算法和KMP算法,分析了它们的工作原理和效率。

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

(或者字符串)是由零个或者多个字符组成的有限序列,一般记为s=‘a1a2a3…an’(n>=0)其中,s是串的名,用单括号括起来的字符串序列是串的值;ai(1<=i<=n)可以是字母、数字或其他字符;串中字符的数目n称为串的长度。零个字符的串称为空串,它的长度为0。



1. 串的概念

1.1 串的定义

串是由零个或者多个字符组成的有序序列。一般记为S=“a1a2…an”(n>=0)。当n=0时称为空串(注意与空格串区分)。

1.2 子串的定义

串中任意个连续的字符组成的子序列称为该串的子串。

只有当两个串的长度相等,并且各个对应位置上的字符都相等时才称这两个串是相等的。

另,串值必须用一对单括号括起来,但单括号本身不属于串,他的作用只是为了避免与变量名或数的常量混淆。

1.3 串与线性表的区别:

(1)串的数据对象限定为字符集;(英文字符——ASCII,中英文——Unicode)

(2)串的基本操作通常以子串为操作对象,而线性表以单个元素作为操作对象。

2. 串的基本操作

2.1 定长顺序储存表示

2.1.1 定义

用一组地址连续的储存单元存储串值得字符序列。在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。

2.1.2 代码如下

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

这里所用的顺序存储的字符串从下标为1的数组分量开始存储,下标为0的分量闲置不用。(本文采用的存储方式)

有的教程采用ch[0]充当length,缺点是只可以表示0-255的长度;还有在串值后面加一个不计入串长的结束标记字符‘\0’,此时的串长为隐含值,缺点是每次需遍历求串长。

2.1.1 串连接

串连接:把两个串s1和s2首尾连接成一个新串s,即s<-s1+s2

2.1.1.1 代码

int StrConcat(s1,s2,s)
    char s1[],s2[],s[];//将串s1,s2合并到串s,合并成功返回1,否则返回0
{
    int i =0,j,len1,len2;
    len1 = StrLength(s1);
    len2 = StrLength(s2);
    
    if(len1+len2>MAXSIZE-1) return 0;    //s 长度不够
 
    j = 0 ;
    while(s1[j]! ="\0"){
        s[i] = s1[j];
        i++;
        j++;
    }
     while(s2[j]! ="\0"){
        s[i] = s2[j];
        i++;
        j++;
    }
    s[i] = "\0";
    return 1;
}

2.1.2 求子串

将串s中从第i个字符开始长度为len的字符序列复制到串s中,

2.1.2.1 代码

int StrSub(char *t ,char *s,int i , in len){
//用t返回串s中第i个字符串开始的长度为len的子串,1<=i串长
    
    int slen;
    slen = StrLength(s);
    if(i<1 || i>slen || len<0 || len>slen-i+1){
        return 0;
    }
    
    for(j =0 ; j<len ; j++){
        t[j] =s[i+j-1];
        t[j] ="\0";
        return 1;
    }
}

2.2 堆分配存储表示

2.2.1 定义

在顺序串上的插入、删除操作并不方便,必须移动大量的字符,而且当操作中出现串值序列的长度超过上界MAXSIZE时,只能用截尾法处理。要克服这个弊病,只有不限定串的最大长度,动态分配串值的存储空间。

堆分配存储结构的特点是:仍以一组地址连续的存储单元存放串的字符序列,但其存储空间是在算法执行过程中动态分配得到的。在C语言中,由动态分配函数malloc()和free()来管理。利用函数malloc()为每一个新产生的串分配一块实际需要的存储空间,若分配成功,则返回一个指针,指向串的起始地址。

由于堆分配存储结构的串既有顺序存储结构的特点,在操作中又没有串长的限制,显得很灵活,因此,在串处理的应用程序中常被选用。

2.2.2 代码如下

typedef struct{
    char *ch;   //按串长分配存储区,ch指向串的基地址
    int length;   //串的长度
}HString;
 
HString S;
S.ch=(char *)malloc(MAXLEN*sizeof(char));
S.length=0;

在C语言中,存在一个称为“堆”的自由存储区,并用malloc()和free()函数来完成动态存储管理。

2.2.3 基本操作

2.2.3.1 赋值操作
//赋值操作
void StrAssign(SString &str)
{
	char cc;
	int i=0;
	scanf("%c",&cc);
	while(cc!='#'&&i<MAXLEN)
	{
		i=i+1;
		str.ch[i]=cc;
		scanf("%c",&cc);
	}
	str.ch[i+1]='\0';  //字符串结束标记
	str.length=i;  //串长为i
}
2.2.3.2 求子串
bool SubString(SString &s1,SString &s,int pos,int len)
{
	if(pos+len-1>s.length)//子串范围越界
		return false;
	for(int i=pos;i<pos+len;i++)
		s1.ch[i-pos+1]=s.ch[i];
	s1.length=len;
	return true;
}
2.2.3.3 比较操作
//比较操作
int StrCompare(SString S,SString T)
{
	//若S>T,则返回值>0;若S=T,则返回值=0,若S<T,则返回值<0
	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;
}
2.2.3.4 定位操作
//定位操作
int Index(SString S,SString T)
{
	int i=1,n=S.length,m=T.length;
	SString sub;//用于暂存子串
	while(i<=n-m+1)
	{
		if(SubString(sub,S,i,m))
		{
			if(StrCompare(sub,T)!=0)
				++i;
			else
				return i;//返回子串在主串的位置
		}
	}
	return 0;//S中不存在与T相等的子串
}

2.3 串的块链存储表示

定义

串的链式存储与线性表相似,但由于串结构的特殊性,结构中的每个元素数据都是一个字符,如果也简单的应用链表存储串值,一个结点对应一个字符,就会存在很大的空间浪费,因此一个结点可以存放多个字符,最后一个结点若是未被占满时,可以用 “#” 或其它非串值字符补全。

为了便于进行串的操作,当以链表存储串值时,除头指针外还可附设一个尾指针指示链表中的最后一个结点,并给出当前串的长度。称如此定义的串存储结构为块链结构。

3.串的模式匹配

1、BF算法(又称古典的、经典的、朴素的、穷举的)

算法思想

(1)将主串的第pos个字符和模式的第一个字符比较,若相等,继续逐个比较后续字符;若不等,从主串的下一字符起,重新与模式的第一个字符比较;

(2)直到主串的一个连续子串字符序列与模式相等 。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。否则,匹配失败,返回值 0。
在这里插入图片描述

//简单模式匹配算法(BF)
int BFIndex(SString S,SString T)
{
	int i=1,j=1;//i,j分别指向主串和模式串
	while(i<=S.length&&j<=T.length)
	{
		if(S.ch[i]==T.ch[j])
		{
			i++;//继续比较后续字符
			j++;
		}
		else
		{
			i++;//检查下一子串
			j=1;
		}
	}
	if(j>T.length)
		return i-T.length;
	else
		return 0;
}
2、KMP算法(特点:速度快)

算法思想

此算法可以在O(n+m)的时间数量级上完成串的模式匹配操作。其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一端距离后,继续进行比较。

模式串的next函数的定义:

next[i][j]=k:

①当j=1(t1与si比较不等时,下一步进行t1与si+1的比较)时Max{k|1<k<j且有"t1t2. . .tk-1"=“tj-k+1tj-k+2. . .tj-1”}

②k=1(不存在相同子串,下一步进行t1与si的比较)

KMP算法描述:

int Index_KMP(Sstring S, Sstring T,int pos)
{
   //利用模式串T的next函数求T在主串S中第pos个字符之后的位置
  //其中,T非空,1<=pos<=S.length
 
   i=pos;j=1;
   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.legnth)return i-T.length;   //匹配成功
   else return 0;   //匹配失败
}

计算next函数值的算法描述:

void get_next(Sstring T,int next[])
{
   //求模式串T的next函数值并存入数组next
   i=1;next[1]=0;j=0;
   while(i<T.length)
   {
      if(j==0||T.ch[i]==T.ch[j]){++i;++j;next[i]=j;}
      else j=next[i][j];
   }
}

计算next函数修正值的算法描述:

void get_nextval(Sstring T,int nextval[])
{
   //求模式串T的next函数修正值并存入数组nextval 
   i=1;nextval[1]=0;j=0;
   while(i<T.length)
   {
      if(j==0||T.ch[i]==T.ch[j])
      {
         ++i;++j;
         if(T.ch[i]!=T.ch[j])nextval[i]=j;
         else nextval[i]=nextval[j];
      }
      else j=nextval[j];
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值