5.数据结构C++学习笔记——串

此篇为本人自己学习过程中的笔记,有许多不足之处,仅供参考。

目录

1.串类型的定义      

2.使用String定义串的抽象数据类型

3. 串的模式匹配算法

3.1 算法目的

3.2 算法应用

3.3 算法种类

3.3.1 BF算法

3.3.2 KMP算法

3.3.3  next函数的改进


1.串类型的定义      

       串是字符串的简称,形象地说就是,把一列字符连成一串作为一个处理对象。 之前学习栈和队列都是线性结构,可以表示为\left ( a_{1},a_{2},...,a_{n} \right ),栈和队列是操作受限的线性表,而串是内容受限的线性表,数据元素只能是字符。在汇编程序和编译程序里面,源程序和目标程序都是字符串数据,在事务处理系统、文字编辑系统、事务问答系统、自然语言翻译系统以及音乐分析等一系列系统中,都是以字符串数据作为处理对象的。

       把由零个或多个任意字符组成的有限数列叫串,如:s="a_{1}a_{2}...a_{n}" n\geqslant 0
       s为串名等号后面双引号里面的字符序列是串的值,n是串的长度,n=0时为空串。空串用\Phi表示。 

子串:串中任意个连续字符组成的子序列(含空串)称为该串的子串
真字串:指不包含自身的所有字串。
主串:包含子串的串称为主串。
“abcde” 的字串有“ ” ,“a”,“ab”,“abc”,“abcd”,“abcde”。

字符位置:字符在序列中的序号为字符在串中的位置。
子串位置:
子串中第一个字符在主串中的位置。
空格串:由一个或多个空格组成的串,
与空串不同
举例:
a="ZHONG",b="GUO",c="ZHONGGUO",d="ZHONG  GUO"
它们的长度分别是5,3,8,9        c的子串是a,b             d的子串是a,b
a在c中的位置1         b在c中的位置6         a在d中的位置6             b在d中的位置7

串相等:当且仅当两个串长度相等,且各个位置上对应的字符都相同时,这两个串才是相等的。

2.使用String定义串的抽象数据类型

ADT String
{
数据对象:D=\left \{ a_{i} | a_{i}\in CharacterSet, i=1,2,...,n,n\geqslant 0 \right \}

数据关系:R=\left \{ <a_{i-1},a_{i} >| a_{i-1},a_{i}\in D, i=1,2,...,n \right \}

基本操作:串赋值,串比较,求串长,串连结,求子串,串拷贝,串判空,清空串,子串的位置
串替换,子串插入,子串删除,串销毁。

}ADT String

串的存储结构,串中的逻辑关系同线性表相同,所以可以采用与线性表相同的储存结构。

       串的顺序存储结构,用数组实现。

#define MAXSIZE 100
class String
{
	char ch[MAXSIZE + 1];  // 储存串的一维数组
	int length;          //串的当前长度
};

      串的链式存储结构:优点操作方便,缺点,存储密度低。存放一个字符占用1个字节,地址需要4个字节。可以将多个字符放在一个节点中,克服缺点。称作块链结构。这实际上是一种顺序储存,链式储存相结合的结构,这种储存结构会增加基本操作的复杂度。例如,对改变串长的操作,可能涉及结点的增加与删除问题。

#define CHUNKSIZE 80   //块的大小由用户自定义
class Node
{
	char ch[CHUNKSIZE];
	Node* next;
};
class Chunk   //字符串的块链结构
{
	Chunk* head, * tail;  //串的头指针与尾指针
	int curlen;    //串的长度
};

3. 串的模式匹配算法

3.1 算法目的

确定主串中所含子串(模式串)第一次出现的位置(定位)

3.2 算法应用

搜索引擎、拼写检查、语言翻译、数据压缩

3.3 算法种类

3.3.1 BF算法

Brute-Force, 又称古典的,经典的,朴素的,穷举的,简单匹配算法,采用穷举法的思路。

      BF算法设计思想,BF从主串S中下标为i的字符与模式串P的第一个字符a开始逐个比较,遇到不相等时,即达到失配点,该趟匹配失败,S回到原来的i+1的位置,P回到第1个字符位置,继续下一趟匹配。直到匹配成功,返回P的第1个字符在S中的位置结束;或S中不存在P,匹配失败返回0结束。图为简单模式匹配算法的示意图,其中i和j分别为主串S和模式串P的下标。最后一趟匹配失败后,无须进行下一次匹配,因为主串中的字符数已经小于子串的串长。

图3.3.1 串的简单匹配算法

int String::Index_BF(String s, String p,int n)
{
	int i = n, j = 1;
	while (i <= s.length && j <= p.length)
	{
		if (s.ch[i] == p.ch[j])
		{
			i++;
			j++;//主串和子串依次匹配下一个字符
		}
		else  // 主串子串指针回溯
		{
			//j-1是移动了多少,i-(j-1)指回到之前的位置,再+1则向后又移一位
			i = i - (j - 1) + 1;
			j = 1;
		}

		if (j >= p.length)
		{
			return i - p.length;// 返回匹配成功第一个字符串的下标
		}
		else
		{
			return 0;   //模式匹配不成功
		}
	}

主串长为n,子串长为m,可能匹配成功的位置为1~n-m+1。

最好的情况下,第i个位置匹配成功,比较了i-1+m次,平均比较次数

\sum_{n-m+1}^{i=1}p_{i}(i-1+m)=\frac{1}{n-m+1}\sum_{i=1}^{n-m+1}(i-1+m)=\frac{1}{2}(m+n)

算法的时间复杂度为O_{(n+m)}

最坏的情况下,第i个位置匹配成功,比较了i×m次,平均比较次数

\sum_{i=1}^{n-m+1}p_{i}(i\times m)=\frac{m}{n-m+1}\sum_{i=1}^{n-m+1}=\frac{1}{2}m(n-m+2)

算法的时间复杂度为O_{(n\times m)}

3.3.2 KMP算法

      是由D.E.Knuth、J.H.Morris、V.R.Pratt共同提出的算法。该算法较BF算法由较大改进,主要是消除了主串指针的回溯,从而提高了算法的效率。提速到O_{(n+m)}

      在3.3.1 的a图中,我们可以看到当第一趟匹配达到失配点时,只是子串中第五个字符匹配失败,前四个都已经匹配成功,所以i和j回溯到0,就显得毫无意义,跳过第2,3次的匹配,直接进行第四趟,在第一趟匹配结束时i=4,失配点前的字符是相等的,S[3]=P[0],所以j不用重新回溯到0,回溯到0只是把两个相等的字符比较,j只要回溯到1,继续比较后面的字符,就可以得到匹配结果。这样i只进不退,就消除了主串的回溯。next[j]=\left\{\begin{matrix}0 & \\ max\left \{ k | 0<k<j ,p_{0}p_{1}...p_{k-1}=p_{j-k+1} p_{j-1}\right \} & \\ 1 & \end{matrix}\right.

 图3.3.2.1 回溯示意图

      实现KMP算法的关键为:j要回溯到哪一个位置,设j要向前回溯k个位置,k是失配时,j需要向前回溯的最少位置。下一趟比较从S[i]与P[k]开始。为此,定义next[j]函数(失败函数),表明当模式中第j个字符与主串中相应字符失配时,在模式中重新和主串中该字符进行比较的字符位置。

  图3.3.2.2 next函数

图3.3.2.3 函数举例

       模式P="abcaabbcabcaabda"的函数如图所示,当j=1时,next[j]为0,当j=2时,前面只有一个字符,没有前缀,也没有后缀,为其他情况,next[j]=1,当j=3时,既没有相等的前缀后缀,j也不为1,仍然属于其他情况,next[j]=1;接下来看j=14时,相同的前缀、后缀子串有"a","abcaa",根据函数定义,取最大值5,此时k-1=5,k值为6。

void GetNext(String p, int & next[])
{
	int k = 1,j = 0 ;
	next[1] = 0;
	while (k < p.length)
	{
		//当k=0或ch[i]与ch[j]相等时,i,j各扩展一位,将求得的j值存放在next[i]中
		if (k==0||p.ch[k]==p.ch[j])
		{
			++k;
			++j;
			next[j] = k;
		}
		else
		{
			k = next[j]; //k回溯到next[j],重新比较
		}
	}
}

3.3.3  next函数的改进

1.第一位的nextval值必定为0,第二位如果于第一位相同则为0,如果不同则为1。
2.第三位的next值为1,那么将第三位和第一位进行比较,均为a,相同,则第三位的nextval值为第一位的next值,为0。
3.第四位的next值为2,那么将第四位和第二位进行比较,不同,则第四位的nextval值为其next值,为2。
4.第五位的next值为2,那么将第五位和第二位进行比较,相同,第二位的next值为1,则继续将第二位与第一位进行比较,不同,则第五位的nextval值为第二位的next值,为1。
5.第六位的next值为3,那么将第六位和第三位进行比较,不同,则第六位的nextval值为其next值,为3。
6.第七位的next值为1,那么将第七位和第一位进行比较,相同,则第七位的nextval值为0。
7.第八位的next值为2,那么将第八位和第二位进行比较,不同,则第八位的nextval值为其next值,为2。

void GetNextUp(String p, int & next[])
{
	int k = -1,j = 0 ;
	next[0] = -1;
	while (k < p.length)
	{
		//当k=-1或ch[i]与ch[j]相等时,i,j各扩展一位,将求得的j值存放在next[i]中
		if (k == -1 || p.ch[k] == p.ch[j])
		{
			++k;
			++j;
			if (p.ch[k] == p.ch[j])
			{
				next[j] = next[k];
			}
			else
			{
				k = next[j];
			}
		}
		else
		{
			k = next[k];
		}	
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值