串的定长顺序存储表示和实现

串是零个或多个字符的有限序列,我们可以将其看作是种特殊的线性表,其特殊性在于线性表的数据元素的类型总是字符性,字符串的数据对象约束为字符集。
串的存储方式有三种:定长顺序存储表示、堆分配存储表示、块链存储表示。
本文主要讲解串的定长顺序存储表示及其操作的实现

相关定义及准备

#include<iostream>
using namespace std;
#define TRUE 1
#define OK 1
#define ERROR 0
#define FALSE 0
#define MAXSTRLEN 255   //串的最大存储容量定义为255
typedef unsigned char SString[MAXSTRLEN + 1];   //定义串的类型别名
typedef int status;
//串的定长顺序存储表示,SString[0]用来存储串的元素个数

SString[0]用来存储串的元素个数
SString[0]表示的是串长

创建一个串

status STrAssign(SString& T, char* chars)
{ //创建一个值等于chars的串
	int i;
	if (strlen(chars) > MAXSTRLEN)  return ERROR;
	T[0] = strlen(chars);
	for (i = 1; i <= T[0]; i++)
	{
		T[i] = *(chars + i - 1);
		return OK;
	}
}

判断串是否为空串

status StrEmpty(SString S)
{   //判断S是否为空串
	if (S[0] == 0) return TRUE;
	else
		return FALSE;
}

输出串

void StrPrint(SString T)
{  //输出字符串T
	int i;
	for (i = 1; i <= T[0]; i++)
		cout << T[i];
	cout << endl;
}

返回串的元素个数

status Strlength(SString S)
{  //返回串的元素个数
	return S[0];
}

将串清空

status ClearString(SString S)
{ //将S清为空串 
	S[0] = 0;              /* 直接令串长为零 */
	return OK;
}

将串S复制到串T中

status StrCopy(SString& T, SString& S)
{   //由串S复制得串T
	int i;
	for (i = 1; i <= S[0]; i++)
		T[i] = S[i];
	T[0] = S[0];
	return OK;
}

比较串S和串T

若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0
status StrCompare(SString S, SString T)
{ 
	if (StrEmpty(S) || StrEmpty(T)) return ERROR;
	int i;
	for (i = 1; i <= S[0] && i <= T[0]; i++)      
	{
		if (S[i] != T[i]) return S[i] - T[i];    //如果不相等,直接返回不相等的值的相减
	}
	return S[0] - T[0];                          //如果字符全部相等,返回他们字符长度的差值
}

删除串中第pos个字符起长度为len的子串

status StrDelete(SString S, int pos, int len)
{ 
	// 从串S中删除第pos个字符起长度为len的子串 
	int i;
	if (pos<0 || pos>S[0] - len + 1 || len < 0)
		return ERROR;
	for (i = pos + len; i <= S[0]; i++)
		S[i - len] = S[i];
	S[0] -= len;
	return OK;
}

在串的第pos个字符之后插入串T

status StrInsert(SString &S, int pos, SString T)
{  //在串S的第pos个字符之后插入串T。完全插入返回TRUE, 部分插入返回FALSE
	int i;
	if (pos<0 || pos>MAXSTRLEN) return ERROR;
	if(S[0] + T[0] <= MAXSTRLEN)
	{
		for (i = S[0]; i >= pos + T[0]; i--)
			S[i + T[0]] = S[i];
		for (i = pos; i < pos + T[0]; i++)
			S[i] = T[i - pos + 1];
		S[0] = S[0] + T[0];
		return TRUE;
	}
	else
	{  //部分插入
		for (i = MAXSTRLEN; i >= pos+T[0]; i--)
			S[i] = S[i - T[0]];
		for (i = pos; i < pos + T[0]; i++)
			S[i] = T[i - pos + 1];
		S[0] = MAXSTRLEN;
		return FALSE;
	}
}

返回串中第pos个字符起,长度为len的子串

status SubString(SString& Sub, SString S, int pos, int len)
{//求串S中第pos个字符起,长度为len的子串
	int i;
	if (pos<0 || pos>S[0] || len<0 || len>S[0] - pos + 1)
		return ERROR;
	for (i = pos; i <= MAXSTRLEN; i++)
	{
		Sub[i - pos + 1] = S[i];
		Sub[0] = len;
	}
	return OK;
}

联接两个串组成新串

status Concat(SString& T, SString s1, SString s2)     
//两串用T返回S1和S2联接而成的新串。若未截断,则返回TRUE,否则FALSE
{
	int i;
	if (s1[0] + s2[0] <= MAXSTRLEN)  //串一和串二长度的总和小于主串的最大容量,未截断
	{
		for (i = 1; i <= s1[0]; i++)     T[i] = s1[i];
		for (i = 1; i <= s2[0]; i++)     T[s1[0] + i] = s2[i];
		T[0] = s1[0] + s2[0];
		return TRUE;
	}
	else if (s1[0] < MAXSTRLEN)    //两串的长度的总和超过最大容量,串一未超过,串二被截断
	{
		for (i = 1; i <= s1[0]; i++)   T[i] = s1[i];
		for (i = 1; i < MAXSTRLEN - s1[0]; i++)  T[s1[0]+i] = s2[i];
		T[0] = MAXSTRLEN;
		return FALSE;
	}
	else                         //两串均超过最大容量,均被截断,取串一中的元素
	{
		for (i = 1; i <= MAXSTRLEN; i++)   T[i] = s1[i];
		T[0] = MAXSTRLEN;
		return FALSE;
	}
}

串的模式匹配(查找子串T在主串S中第pos个字符之后的位置)

status Index(SString T, SString S, int pos)    
{   //索引子串T在主串S中第pos个字符之后的位置。若不存在,则函数值为0。
	int i, j;
	if (pos<0 || pos>S[0]) return ERROR;
	else
	{
		i = pos;
		j = 1;
		while(i<=S[0]&&j<=T[0]) 
			if (S[i] = T[j])    //如果匹配就继续往后比较
			{
				i++; 
				j++;
			}
			else
			{
				i = i - j + 2;
				j = 1;
			}
		if (j > T[0])
			return i - T[0];
		else return 0;
	}
}     

此方法的时间复杂度太大,故采用了一种改进的方法:KMP算法
具体思路如下:
在这里插入图片描述
在这里插入图片描述
故引入模式串中第j个字符的下一个待比较的位置 定义为

next[j]

在这里插入图片描述
注意:后一个字符的next值比前一个字符的next值最多大1
模式串的第一个字符的next值为0,第二个为1,后面的需要根据定义依次判断

例如:
在这里插入图片描述
关于next[j]值的计算思想,我认为西安邮电大学王燕老师的讲解很是详细!
西安邮电大学慕课

串的模式匹配KMP算法

分两部分:next值计算函数和kmp算法

next[j]的获取

void get_next(SString S, int next[])    //改进的模式匹配KMP算法中的next[j]的获取
{
	int i, j;
	int tlen = Strlength(S);
	i = 1;
	j = 0;
	next[1] = 0;
	while (i < tlen) {
		if (j == 0 || S[i] == S[j]) /*t[i]表示后缀的单个单词,t[j]表示前缀的单个单词*/
		{
			++i;
			++j;
			next[i] = j;
		}
		else {
			j = next[j];  /*若字符串不相同,则j值回溯*/
		}
	}
}

KMP算法

status IndexKPM(SString T, SString S, int pos,int next[])
{   //索引子串T在主串S中第pos个字符之后的位置,改进中添加了next[j]

	int i=pos, j=1;
	get_next(S, next);
	while (i <= S[0] && j <=T[0])
	{
		if (S[i] == T[j])
		{
			i++;
			j++;
		}
		else  j = next[j];
	}
	if (j > T[0]) return i - T[0];
	else return 0;
}

当你自堕深渊,只为寻得阴光,甘愿被吞噬时,我也许该杀死那个你了

“ 你本来是有机会的,但是你输了,你不能总是活在过去 ” ——派大星

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值