【数据结构】-- 串(BF、KMP算法)

计算机上的非数值处理的对象大部分是字符串数据,字符串一般简称为串(string)

串的定义

串(string)(或字符串)是由零个或多个字符组成的有限序列。

串的概念解释:

  • 空串(null string):零个字符的串。
  • 子串:串中任意个连续的字符组成的子序列,子串在主串中的位置则以子串的第一个字符在主串中的位置来表示。
  • 主串:包含子串的串相应的称为主串。
  • 空格串:只包含空格的串
  • 相等串:位数相等,对应每个位置的字符相同

串的抽象类型定义

串的逻辑结构和线性表相似,区别仅在于串的数据对象约束为字符集。然而,串的基本操作和线性表有很大区别。在线性表中通常以“单个元素”作为操作对象;而在串中,通常以“串的整体”作为操作对象

ADT String
{
	Data 
		串中元素仅由一个字符组成,有前驱后继的关系
	Operation
		StaAssign(&T,chars)	生成一个其值等于chars的串T
		StrCopy(&T,S)	由串S复制得串T
		ClearString(&T)	将串T清空
		StrEmpty(S)		若串S为空,返回true
		StrCompare(S,T)	 若S>T 其返回值大于0,S=T 返回值=0,S<T 返回值<0
		StrLength(S)	返回字符串S的长度
		Concat(&T,S1,S2)	用T返回由S1、S2组成的新串
		SubString(&Sub,S,pos,len)	用Sub返回串S的第pos个字符起长度为len的子串
		Index(S,T,pos)	若主串S中存在和串T值相同的子串,则返回它在主串中第pos个字符后
						第一次出现的位置,否则函数值为0
		Replace(&S,T,V)	用V替换主串S中出现的所有与T相等的不重叠的子串
		StrInsert(&S,pos,T)	在串S第pos个位置之前插入串T
		StrDelete(&S,pos,len)	在串S中删除第pos个字符起长度为len的子串 
}

串的存储结构

1、串的顺序存储结构

类似于线性表,用一组地址连续的存储单元存储串值的字符序列。

-----串的定长顺序存储结构-----
#define MAXLEN 255
typedef struct{
	char ch[MAXLEN+1];		//存储串的一维数组
	int length;			//串的当前长度 
}SString; 

注:以上定义方式是静态的,再编译的时候就确定了串的空间大小。根据实际需要,串的变化是很大的,因此最好的是在程序执行过程中动态的分配和释放字符数组空间。所以有了——“堆”(Heap)

------串的堆式存储结构------ 
typedef struct{
	char *ch;	        //若是非空串,则按串长分配存储区,否则ch为NULL
	int length;		//串的当前长度 
}HString;

2、串的链式存储结构

对于串结构的特殊性——结构中的每个数据元素是一个字符,则在链表存储串值时,会造成空间的利用率不足、即“结点大小”的问题。每个结点可以存储一个字符,也可以存放多个字符

------串的链式存储结构------
#define CHUNKSIZE 80		//可由用户定义的块大小
typedef struct Chunk{
	char ch[CHUNKSIZE];
	struct Chunk *next;
}Chunk;
typedef struct{
	Chunk *head,*tail;	//串的头尾指针
	int length;		//串的当前长度 
}LString; 

特此声明:以下算法均利用串的顺序存储模式进行示例。


串的模式匹配算法

子串的定位运算通常称为串的模式匹配或串匹配。

假设我们要从主串S=“goodgoogle”,找到T=“google”这个子串的位置。如果匹配成功,则返回子串的第一个字符在主串中的位置。所以有了两种著名的算法:BF算法与KMP算法

模式匹配算法不一定从第一个位置开始,可以指定主串中查找的起始位置pos。

1、BF算法(Brute-Force)     O(m*n)

特点:古典、直观、经典、朴素、穷举的

【算法步骤】

①分别利用计数指针i与j 分别指向pos与1。

②当i<=S.length并且j<=T.length时执行以下操作

  • 如果i指向S当前字符等于j指向T当前字符,则i和j分别指向下一个位置。
  • 否则,i和j归位即 (i=i-j+2)

③若j大于T的长度,那么匹配成功,返回i-T.length,否则返回0

int Index_BF(SString S,SString T,int pos)
{
	int i=pos,j=1;
	while(i<=S.length&&j<=T.length)
	{
		if(S.ch[i]==T.ch[j])
		{
			i++;
			j++;
		}
		else 
		{
			i=i-j+2;
			j=1;
		}
	}
	if(j>T.length)
		return i-T.length;
	else 
		return 0;
}

【算法分析】

注:这次没有gif……懒得做了……所以我用了excel  哈哈哈哈

BF算法匹配步骤
第一趟匹配goodgoogle
google    
第二趟匹配goodgoogle
 google   
第三趟匹配goodgoogle
  google  
第四趟匹配goodgoogle
   google 
第五趟匹配goodgoogle
    google

由上图可知,最好的情况是从第一个字符就匹配成功了,最坏的情况就如上表所示,到最后才匹配成功。所以可得BF算法的时间复杂度:O(m*n)。

2、KMP算法     O(m+n)

KMP算法是由Knuth、Morris和Pratt同时设计的,因此简称KMP算法。这种算法进行了改进,时间效率提高了许多。

KMP算法分为了两个部分,第一个部分是求next[],第二个部分是匹配……,相比较于BF算法,KMP算法省略了上表的第二、三、四趟比较。并且指向主串的指针不回溯。。所以kmp算法的精力主要集中在 子串的处理。。来吧……上代码……

//计算next函数值 
void get_next(SString T,int next[])
{
	int i=1,j=0;
	next[1]=0;
	while(i<T.length)
	{
		if(j==0||T.ch[i]==T.ch[j])
		{
			j++;i++;
			next[i]=j;
		}
		else 
			j=next[j];
	}
}

//计算next函数修正值 
void get_nextval(SString T,int nextval[])
{
	int i=1,j=0;
	nextval[1]=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];
	}
}

//KMP算法 
int Index_KMP(SString S,SString T,int pos)
{
	int 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.length)	
		return i-T.length;
	else
		return 0;
}

等等!!!结尾附上next函数定义:


博客内容借鉴于:

①《数据结构》  作者:严蔚敏

②《大话数据结构》 作者:程杰

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值