串是零个或多个字符的有限序列,我们可以将其看作是种特殊的线性表,其特殊性在于线性表的数据元素的类型总是字符性,字符串的数据对象约束为字符集。
串的存储方式有三种:定长顺序存储表示、堆分配存储表示、块链存储表示。
本文主要讲解串的定长顺序存储表示及其操作的实现
相关定义及准备
#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]用来存储串的元素个数
创建一个串
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;
}
当你自堕深渊,只为寻得阴光,甘愿被吞噬时,我也许该杀死那个你了
“ 你本来是有机会的,但是你输了,你不能总是活在过去 ” ——派大星