数据结构——串

一:基本概念

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

       一般记为 s = "a1 a2 a3......an"(n>= 0), 其中 s 是串名;引号括起来的字符序列是
       串值(串值可以是字母、数字或其它字符);串中的字符数目n称为串的长度(串长)
    
  • 空串(Null string):零个字符的串称为空串,其长度为零。

  • 子串(SubString):指串中任意个连续的字符组成的子序列。

  • 主串(PriString):包含子串的串相应的被称为主串。

  • 空格穿(blank string):指由一个或多个空格组成的串。

二:串的存储结构

    串是一种特殊的线性表,所以有顺序存储结构和链式存储结构。

1:顺序存储结构

  • 数组——静态顺序存储结构

数据结构——串_第1张图片
对串长变化不大的字符出,有三种处理方法
1)用S[0]作为记录串长的存储单元(Pascal)
- 缺点:限制了串的最大长度不能超过256
2)为存储串的长度,另辟一个存储地方
- 缺点:串的最大长度一般是静态给定的,不是动态申请数组空间
3)用一个特殊的末尾标记’\0’(C/C++)

  • 堆——动态顺序存储结构
    C语言:由动态分配函数malloc()和 free() 管理
    C++:使用new() 和 delete()

2:链式存储结构

串china用链表表示数据结构——串_第2张图片链式存储结构只对字符串连接有方便之处

三:字符串函数

以下函数简单介绍

头文件

  • 字符串符指函数:strcpy(目的字符串, 源字符串)
    功能:把src所指由NUL结束的字符串复制到dest所指的数组中。
#include 
#include 
int main(void)
{
	char *s = "I love China";
	char d[13];
	strcpy(d, s);
	printf("%s", d);
	return 0;
}

  • 字符串连接函数:strcat(dsr, src)
    功能:把src所指字符串添加到dest结尾处(覆盖dest结尾处的’\0’)并添加’\0’。
#include 
#include 
int main(void)
{
	char d[13] = "I love my ";
	char s[7] = "school";
	strcat(d, s);
	printf("%s", d);

	return 0;
}

  • strlen(s)
    功能:计算字符串s的长度,不包括结束符NULL
#include 
#include 

int main(void)
{
	char *s = "I am from China";
	printf("%d", strlen(s));
	return 0;
}
  • strncat(dsr, src, n)
    功能:把src所指字符串的前n个字符添加到dest结尾处(覆盖dest结尾处的’\0’)并添加’\0’。
#include 
#include 

int main(void)
{
	char d[13] = "I am a girl";
	char s[10] = "only one";
	strncat(d, s, 6);
	printf("%s", d);
	return 0;
}
  • strcmp
    功 能: 将一个串与另一个比较
#include 
#include 

int main(void)
{
	char d[4] = "bbb", s[4] = "BBB";
	int res;
	res = strcmp(d, s);
	if (res > 0)
		printf("1");
	else if(res < 0)
		printf("-1");
	else
		printf("0");
	return 0;
}

四:串的模式匹配算法

1:模式匹配及其BF算法(又叫朴素模式匹配,穷举法)

  • 子串的定位操作通常称为串的模式匹配

  • 子串被称为模式串

  • 模式匹配最简单、最直观的算法是BF(Brute-Force)算法

    BF算法的主要思路:

    在主串s中取从第i个字符起、长度和串t相等的子串和串t比较,若相等,则求得函数值为正,否则i值增1直至串s中不存在和t相等的子串为止。

    BF算法的时间复杂度:

    n : 主串长
    m :子串长
    时间复杂度:O((n - m + 1)*m)

    • 最好情况:第一趟比较成功
    • 最坏情况:每一趟都在最后出现不等

    步骤图解:
    假设 主串 s = “goodgoole” 子串 t = “google”
    数据结构——串_第3张图片
    BF算法的代码展示:

int index(char s[], char t[], int startpos)
{
	int lastpos = strlen(s) - strlen(t);
	int i = startpos, j = 0;
	if(lastpos < startpos)
		return -1;
	while(i < strlen(s) && j < strlen(t)
	{
		if(t[j] == s[i])
		{
			i ++;
			j ++;
		}
		else
		{
			i = i - j + 1;//i退回到上次匹配首位的下一位
			j = 0;
		}
	}
	if(j >= strlen(t))
		return (i-j);//若匹配成功,则返回子串的开始位置
	else
		return -1;
}




2:KMP模式匹配算法

这张图是我在看网课时的截屏
数据结构——串_第4张图片

当然在这里,相信许多人和最开始的我一样,刚学KMP整个人是懵的。
按我自己学完后的感受,我先介绍 next数组推导

next数组

  • 定义:把t串(子串)各个位置的 j值的变化定义为一个数组 next, 那么 next 的长度就是 t串的长度。如下函数定义:
    数据结构——串_第5张图片
    当然,此处的函数定义有各种版本,但本质都是一样的。

  • next 数组值推导

    1):具体如何推导一个串的next数值组,先看一些例子
    提前告知相关计算:如果前后缀一个字符相等,K值是2,两个字符相等,K值是3,n个相等K值就是n+1
    数据结构——串_第6张图片
    数据结构——串_第7张图片

    相信大家对于KMP模式匹配算法的步骤图再看书的时候都能看懂,这里就不再赘述

代码展示

  • 举例
    目标串是t,模式串是s
    数据结构——串_第8张图片

数据结构——串_第9张图片
在第一次匹配时,t[5] != s[5],子串j=5时 next[5] = 3,所以让子串s的第三个字符与主串t第5个字符依次匹配; 由于t[5] != s[3],子串j=3时 next[3] = 1,所以让子串s的第一个字符与主串t第5个字符依次匹配;依次继续(当遇到next[1] = 0时,主串 下移一个字符)。

  • 代码
/*通过计算返回子串s的next数组*/

void get_next(char s[], int *next)
{
	int i, j;
 	i = 1;
 	j = 0;
 	next[1] = 0;
 	while(i < s[0])//s[0]表示串t的长度
 	{
  		if(j == 0 || s[i] == s[j])//s[j]表示前缀的单个字符,s[i]表示后缀的单个字符 
  		{
   			++ i;
   			++ j;
   			next[i] = j;
  		}
  		else
   			j = next[j];//若字符不相同,则j值回溯
 	}
 }
/*返回子串s在主串t中第pos个字符之后的位置。若不存在,则函数值返回0*/
/*s非空, 1 <= pos <= strlen(t)*/
int Index_KMP(char t[], char s[], int pos)
{
	int i = pos;
 
 	int j = 1;
 
 	int next[255];
 
 	get_next(s, next);
 	while (i <= s[0] && j <= s[0])
 	{
  		if(j == 0 || t[i] == s[j])
  		{
   			++ i;
   			++ j;
  		}
  		else
   		j = next[j];
   
 	}
 	if(j > s[0])
  		return i-s[0];
 	else 
  		return 0;
 

3:KMP匹配算法改进

如果主串s = “aaaabcde”, 子串 t = " aaaaax",其next数值分别为012345。按照KMP匹配算法,此处会多次回溯匹配。这种情况的出现,所以对KMP匹配算法进行改进。

nextval数组

数据结构——串_第10张图片

代码展示

void get_nextval(char s[], int *nextval)
{
	int i, j;
 	i = 1;
  	j = 0;
  	nextval[1] = 0;
  	while(i < s[0])//s[0]表示串t的长度
  	{
  		if(j == 0 || s[i] == s[j])//s[j]表示前缀的单个字符,s[i]表示后缀的单个字符 
   		{
      			++ i;
      			++ j;
      			if(s[i] != s[j])
      				nextval[i] = j;
      			else
      				nextval[i] = nextval[j];
    		}
    		else
    			j = nextval[j];//若字符不相同,则j值回溯
  	}
 }

     

实现匹配算法,只需将 get_next(s, next) 改为 get_nextval(s, nextval)

你可能感兴趣的:(数据结构,算法,c语言)