前几次我们介绍了栈和队列两种特殊的线性表,它们都是操作位置受限的线性表:限制了插入和删除操作发生的位置。
这次我们介绍另一种特殊的线性表,它虽然没有操作位置限制,但却有元素类型限制:它限制元素类型只能是字符,这样的线性表我们称之为串。
还是老规矩:
程序在码云上可以下载。
地址:https://git.oschina.net/601345138/DataStructureCLanguage.git
串可以进行很多操作,有很多有特色的操作是线性表中没有的,比如:串的模式匹配。
模式匹配算法分为BF和KMP算法两种,本次程序将基于实现的顺序串继续实现这两种模式匹配算法。
首先看看串的ADT定义:
ADT String {
数据对象:D ={ai | ai∈Characterset,(i=1,2,…,n, n≥0)}
数据关系:R1 = {<ai-1,ai>|ai-1,ai ∈ D,(i=2,3,…,n)}
基本操作:
StrAssign (&T, chars)
初始条件:chars 是字符串常量。
操作结果:把 chars 赋为T的值。
StrCopy (&T, S)
初始条件:串 S 存在。
操作结果:由串 S 复制得串T。
DestroyString (&S)
初始条件:串 S 存在。
操作结果:串 S 被销毁。
StrEmpty (S)
初始条件:串 S 存在。
操作结果:若 S 为空串,则返回true,否则返回 false。
StrCompare (S, T)
初始条件:串 S和T存在。
操作结果:若S > T,则返回值> 0;若S = T,则返回值= 0;若S < T,则返回值< 0。
StrLength (S)
初始条件:串 S 存在。
操作结果:返回 S 的元素个数,称为串的长度。
Concat (&T, S1, S2)
初始条件:串 S 1和S2存在。
操作结果:用T返回由S1和S2联接而成的新串。
SubString (&Sub, S, pos, len)
初始条件:串 S 存在,1≤pos≤StrLength(S) 且 0≤len≤StrLength(S)-pos+1。
操作结果:用Sub返回串S的第 pos 个字符起长度为len的子串。
Index (S, T, pos)
初始条件:串S和T存在,T是非空串,1≤pos≤StrLength(S)。
操作结果:若主串 S 中存在和串 T 值相同的子串, 则返回它在主串 S 中第pos个字符之后第一次出现的位置;否则函数值为0。
Replace (&S, T, V)
初始条件:串S, T和 V 均已存在,且 T 是非空串。
操作结果:用V替换主串S中出现的所有与(模式串)T相等的不重叠的子串。
StrInsert (&S, pos, T)
初始条件:串S和T存在,1≤pos≤StrLength(S)+1。
操作结果:在串S的第pos个字符之前插入串T。
StrDelete (&S, pos, len)
初始条件:串S存在1≤pos≤StrLength(S)-len+1。
操作结果:从串S中删除第pos个字符起长度为len的子串。
ClearString (&S)
初始条件:串S存在。
操作结果:将S清为空串。
} ADT String
在了解了串可以进行哪些基本操作之后,一起来看看这些操作的实现以及两种模式匹配算法的实现:
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>引入头文件<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#include <stdio.h> //使用了标准库函数 printf(),scanf()
#include <stdlib.h> //使用了动态内存分配函数 malloc(),free()
#include <string.h> //使用了字符串处理函数strlen()
//>>>>>>>>>>>>>>>>>>>>>>>>>>>自定义符号常量<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#define OVERFLOW -2 //内存溢出错误常量
#define OK 1 //表示操作正确的常量
#define ERROR 0 //表示操作错误的常量
#define TRUE 1 //表示逻辑正确的常量
#define FALSE 0 //表示逻辑错误的常量
#define MAXSTRLEN 255 //最大串长
#define DestoryString ClearString //DestoryString()和ClearString()作用相同
//>>>>>>>>>>>>>>>>>>>>>>>>>>>自定义数据类型<<<<<<<<<<<<<<<<<<<<<<<<<<<<
typedef int Status; //状态参量
typedef char ElemType; //元素类型
// 串的定长顺序存储C语言表示
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度,1号单元开始存储串值字符序列
//--------------------------串的主要操作------------------------------
/*
函数:Print
参数:ElemType e 待访问元素
返回值:无
作用:访问元素,也就是打印输出
*/
void Print(ElemType e){
//指定遍历方式为控制台输出
printf("%c",e);
}//Print
/*
函数:StrTraverse
参数:SString T 待访问元素
void(* Visit)(ElemType) 函数指针,可以指向一个函数
返回值:无
作用:(串遍历)打印输出串的内容
*/
void StrTraverse(SString T, void(* Visit)(ElemType)){
//依次遍历每个元素
for(int i = 1; i <= T[0]; i++) {
Visit(T[i]);
}//for
//输出换行,使结果美观
printf("\n");
}//StrTraverse
/*
函数:StrAssign
参数:SString T 定长串,采用我们自定义的数据类型,0号单元存放串长,
字符数据从1号单元开始存储。
chars 指向存储字符类型数据的指针,即指向一个已经存在的字符串,
这个串采用C语言默认的方式,数据从0号单元开始存储,以'\0'作为结束标志。
返回值:状态码,OK表示操作成功
作用:(生成串)生成一个其值等于chars的串T
*/
Status StrAssign(SString T, char *chars){
int i;
//越界判断,判断串长是否超过最大值
if(strlen(chars) > MAXSTRLEN){
//操作失败
return ERROR;
}//if
else{
//0号存储单元存储了串的长度
T[0] = strlen(chars);
//复制chars指向的字符串的内容到串T
for(i = 1; i <= T[0]; i++) {
//i是从1开始的,所以要-1,因为chars的字符数据是从0开始的
T[i] = *(chars + i - 1);
}//for
//操作成功
return OK;
}//else
}//StrAssign
/*
函数:StrCopy
参数:SString T 复制后得到的串(目的地)
SString S 被复制的串(源)
返回值:无
作用:(串拷贝)由串S复制得到串T
*/
void StrCopy(SString T, SString S){
//以串S为基准
for(int i = 0; i <= S[0]; i++){
//串S和串T相同位置对拷
T[i] = S[i];
}//for
}//StrCopy
/*
函数:StrEmpty
参数:SString S 目标串
返回值:状态码,TRUE表示串S是空串,ERROR表示串S不是空串
作用:(判空串)若S为空串,则返回TRUE,否则返回FALSE
*/
Status StrEmpty(SString S){
//0号存储单元记录了串的长度
if(S[0] == 0) { //串空
return TRUE;
}//if
else {
return FALSE;
}//else
}//StrEmpty
/*
函数:StrCompare
参数:SString S 被比较串1
SString T 被比较串2
串S和串T必须存在
返回值:若S > T,返回值 > 0 ,若S = T,返回值 = 0,若S < T,返回值 < 0
作用:(串比较)串S和串T比较,判断大小的依据是ASCII码。
*/
int StrCompare(SString S, SString T){
//如果两串串长相等,则逐个比较每个字符
for(int i = 1; i <= S[0] && i <= T[0]; ++i) {
//通过对两个串中对应位置字符的ASCII码值作差进行比较
if(S[i] != T[i]) {
return S[i] - T[i];
} //if
} //for
//如果两串串长不相等,则直接返回两串串长之差