数据结构与算法(4)--串

Author:Liedra
https://www.cnblogs.com/LieDra/

Ch4 串


0x01 串


定义/基本概念

串(string)是由零个或多个字符组成的有限序列,又叫字符串。(是数据元素为单个字符的特殊线性表)。
串长:串中字符的个数(n≥0)。

空串:串中字符的个数为0时称为空串。

空白串:由一个或多个空格符组成的串。

子串:串S中任意个连续的字符序列叫做S的子串,S叫主串。

子串位置:子串的第一个字符在主串中的序号。

字符位置:字符在串中的序号。

串相等:串长度相等,且对应位置上字符相等。(即两个串中的字符序列一一对应相等。)

注意

  • 空白串与空串不同,空串是指长度为零的串;而空白串是指包含一个或多个空白字符' '(空格等)的字符串。
  • 串与字符不同,“a”和'a'是不一样的。

串与一般的线性表的异同:
串是一种特殊的线性表。
线性表数据元素可以是任意数据类型,而串的数据类型只允许是字符类型。

逻辑结构与线性表相似,但是不同的是串中针对的是多个数据元素(即子串。线性表更关注对单个元素的操作如查找、删除或插入一个元素。而串中更多的是查找子串位置,得到指定位置子串,替换子串等。

串的操作一般有:初始化串,赋值,求串长度,比较两个串,插入子串,删除子串,取子串,查找子串,替换子串等。
下面是C++语言中的串函数:
用C++处理字符串时,要调用标准库函数 #include<string.h>
串长度:int strlen(char *str)
串比较:int strcmp(char *str1,char *str2)
串拷贝:char * strcpy(char *str1,char *str2)
串连接:char * strcat(char *str1,char *str2)
字符定位:char *strchr(char *str,char ch)
子串查找: char *strstr(char *s1,char *s2)

应用

例如外国人的 姓和名前后互换。(比较简单)

存储结构

顺序存储结构

串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列。一般是用定长数组来定义。
可以将实际的串长度值保存在数组0下标位置,也可以放在数组最后一个下标位置。有些语言在串后面加一个如\0一样的不计入串长度的结束标记符来表示串值的终结。
串的顺序存储结构按照内存分配不同可以还分为下面两种

  • 静态数组结构

用静态内存分配方法定义的数组。在运行时数组元素的个数是不可改变的,也被称为定长数组结构。

  • 动态数组结构

用动态内存分配方法定义的数组。

链式存储结构

与线性表相似,但是由于每个元素都是一个字符,如果每个结点存储一个字符,那么会存在很大的空间浪费,因此,一个结点可以考虑存放多个字符。如果最后一个结点未被占满,那么可以使用#或其他非串值字符补全。

所以可分为单字符结点链、块链

注意,串的链式存储结构除了在链接串与串操作时有一定的方便之处,总的来说不如顺序存储灵活,性能也不如顺序存储结构好。

串的模式匹配算法

串的查找操作也称做串的模式匹配操作,其中Brute-Force算法和KMP算法是两种最经常使用的顺序存储结构下的串的模式匹配算法

Brute-Force算法

将主串S的第一个字符和模式T的第1个字符比较,若相等,继续逐个比较后续字符;若不等,从主串S的下一字符起,重新与T第一个字符比较。
直到主串S的一个连续子串字符序列与模式T相等。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。
否则,匹配失败,返回值 –1。
串匹配算法1.png

int String::Find Substr(const String& t, int start)const
{
	int i = start, j = 0, v;
	while(i < size && j < t.size)
	{
		if(str[i] == t.str[j])
		{i++;j++;}
		else
		{i = i-j+1;j = 0;}//主串后移1位继续与字串比较
	}
	if(j >= t.size-1) v = i-t.size+1;
	else v = -1;
	return v;
} 

BF 算法的时间复杂度:
若n为主串长度,m为子串长度,则最坏O(n*m)。

KMP算法(此算法较难理解)

在BruteForce算法的基础上的模式匹配的改进算法。KMP算法的特点主要是消除了Brute-Force算法的如下缺点: 主串下标i在若干个字符序列比较相等后,只要有一个字符比较不相等便需要把下标i的值回退。分两种情况分析Brute-Force

具体算法介绍可参看下列网址:
https://www.zhihu.com/question/21923021

总体而言KMP算法是比较巧妙的,而上述网址中的解释算是一种比较容易理解的方式。

在运行效率上:
对于KMP,由于主串比较位置i无须回退,所以比较次数仅仅是n,而计算next[j]所用的比较次数为m,所以总次数为O(n+m)。(另:next[]数组的值是可以提前算好的)

posted @ 2020-06-23 20:02  LieDra  阅读(1123)  评论(0编辑  收藏  举报