1. 背景说明
串采用定长顺序存储结构实现的方式和字符串的区别在于字符串以字符 '\0' 作为字符结尾的标志,而顺序串采用长度来判断结尾。
1)存储结构示例图
2)算法 4.5 实现过程示例图
3)Next 函数定义:
4)使用 Next 数组匹配过程示意图
5)使用算法 4.7 求 Next 数组示例图
6)算法 4.8 求 Next 数组(优化)与 算法 4.7 对比图
2. 示例代码
1) errorRecord.h
// 记录错误宏定义头文件
#ifndef ERROR_RECORD_H
#define ERROR_RECORD_H
#include <stdio.h>
#include <string.h>
#include <stdint.h>
// Extracts the file name from the file path
#define FILE_NAME(X) strrchr(X, '\\') ? strrchr(X, '\\') + 1 : X
// Define DEBUG macro for enabling debug mode
#define DEBUG
// Print error message
#ifdef DEBUG
#define ERR_RECORD(ERR_CODE, ...) do { \
printf(ANSI_COLOR_BRIGHT_CYAN \
"\n\nFile: %-25s Func: %-20s Line: %-10d ErrorCode: %-8d ErrorInfo: ", \
FILE_NAME(__FILE__), __func__, __LINE__, (ERR_CODE)); \
printf(""__VA_ARGS__); \
printf("\n" ANSI_COLOR_RESET); \
PrintErrorCodeInfo(ERR_CODE); \
} while (0)
#else
#define ERR_RECORD(ERR_CODE, FORMAT, ...)
#endif
// Function result status codes
enum StatusCode {
RET_OK = 0, // Operation successful
ERR_NULL_PTR = 1, // Null pointer error
ERR_MEMORY_ALLOCATE = 2, // Memory allocation error
ERR_EMPTY_STACK = 3, // Empty stack error
ERR_FULL_STACK = 4, // Full stack error
ERR_EMPTY_QUEUE = 5, // Empty queue error
ERR_FULL_QUEUE = 6, // Full queue error
ERR_PARA_ILLEGAL = 7, // Parameter illegal error
ERR_OPEN_FILE = 8, // File open error
ERR_NOT_EXIST = 9, // Not exist error
};
enum BoolCode {
TRUE = 1,
FALSE = 0
};
typedef uint32_t Status;
typedef uint8_t Boolean;
// ANSI 转义码定义
#define ANSI_COLOR_BRIGHT_RED "\x1b[91m"
#define ANSI_COLOR_BRIGHT_YELLOW "\x1b[93m"
#define ANSI_COLOR_BRIGHT_CYAN "\x1b[96m"
#define ANSI_COLOR_RESET "\x1b[0m"
// Function to print error code information with bright colors
static void PrintErrorCodeInfo(int errorCode)
{
switch (errorCode) {
case ERR_NULL_PTR:
printf(ANSI_COLOR_BRIGHT_RED "Null Pointer Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_MEMORY_ALLOCATE:
printf(ANSI_COLOR_BRIGHT_RED "Memory Allocation Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_EMPTY_STACK:
printf(ANSI_COLOR_BRIGHT_RED "Empty Stack Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_FULL_STACK:
printf(ANSI_COLOR_BRIGHT_RED "Full Stack Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_EMPTY_QUEUE:
printf(ANSI_COLOR_BRIGHT_RED "Empty Queue Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_FULL_QUEUE:
printf(ANSI_COLOR_BRIGHT_RED "Full Queue Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_PARA_ILLEGAL:
printf(ANSI_COLOR_BRIGHT_RED "Illegal Parameter Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_OPEN_FILE:
printf(ANSI_COLOR_BRIGHT_RED "File Open Error\n\n" ANSI_COLOR_RESET);
break;
case ERR_NOT_EXIST:
printf(ANSI_COLOR_BRIGHT_RED "Not Exist Error\n\n" ANSI_COLOR_RESET);
break;
default:
printf(ANSI_COLOR_BRIGHT_YELLOW "Unknown Error Code: %d\n\n" ANSI_COLOR_RESET,
errorCode);
break;
}
}
#endif // !ERROR_RECORD_H
2) sqString.h
// 串采用定长顺序存储结构实现头文件
#ifndef SQSTRING_H
#define SQSTRING_H
#include "errorRecord.h"
#define MAX_STR_LENGTH 100
typedef char SqString[MAX_STR_LENGTH + 1];
Status StrAssign(const char *str, SqString string);
Status StrCopy(const SqString str, SqString string);
Status StrEmpty(const SqString str, Boolean *isEmpty);
int StrCompare(const SqString str, const SqString string);
int StrLength(const SqString str);
void ClearString(SqString str);
Boolean Concat(const SqString str1, const SqString str2, SqString string);
Status SubString(const SqString str, int pos, int len, SqString sub);
int Index(const SqString mainStr, const SqString subStr, int pos);
Status StrInsert(const SqString string, int pos, SqString str, Boolean *isFullInsert);
Status StrDelete(int pos, int len, SqString str);
Status Replace(const SqString string, const SqString strReplace, SqString str);
void DestoryString(void);
void StrPrint(SqString string);
#endif // !SQSTRING_H
3) sqString.c
// 串采用定长顺序存储结构实现源文件
#include "sqString.h"
/*
前置条件:str 非空指针,string 存在
操作结果:生成一个其值等于 str 的串 string,该串最大长度为 MAX_STR_LENGTH - 1,
string[0] 存储字符串长度,留一个给空字符
*/
Status StrAssign(const char *str, SqString string)
{
if (!str) {
ERR_RECORD(ERR_NULL_PTR);
return ERR_NULL_PTR;
}
int length = (int)strlen(str);
if (length > MAX_STR_LENGTH) {
ERR_RECORD(ERR_PARA_ILLEGAL, "length = %d", length);
return ERR_PARA_ILLEGAL;
}
string[0] = length;
for (int i = 1; i <= string[0]; ++i) {
string[i] = *(str + i - 1);
}
return RET_OK;
}
/*
前置条件:串 str 存在
操作结果:由串 str 复制得串 string
*/
Status StrCopy(const SqString str, SqString string)
{
for (int i = 0; i <= str[0]; ++i) {
string[i] = str[i];
}
return RET_OK;
}
/*
前置条件:isEmpty 非空
操作结果:用 *isEmpty 返回串是否为空串
*/
Status StrEmpty(const SqString str, Boolean *isEmpty)
{
if (!isEmpty) {
ERR_RECORD(ERR_NULL_PTR);
return ERR_NULL_PTR;
}
*isEmpty = (str[0] == 0) ? TRUE : FALSE;
return RET_OK;
}
/*
前置条件:串 str 和 string 存在
操作结果:若 str > string,则返回值 > 0; 若str = string,则返回值 = 0;
若 str < string,则返回值 < 0
*/
int StrCompare(const SqString str, const SqString string)
{
for (int i = 1; (i <= str[0]) && (i <= string[0]); ++i) {
if (str[i] != string[i]) {
return str[i] - string[i];
}
}
return str[0] - string[0];
}
/*
前置条件:str 存在
操作结果:返回串的元素个数
*/
int StrLength(const SqString str)
{
return str[0];
}
/*
前置条件:str 存在
操作结果:将 str 清为空串
*/
void ClearString(SqString str)
{
str[0] = 0;
}
/*
算法 4.2
前置条件:str1,str2 存在
操作结果:用 string 返回 str1 和 str2 联接而成的新串,
若未截断,则返回 TRUE,否则返回 FALSE
*/
Boolean Concat(const SqString str1, const SqString str2, SqString string)
{
for (int i = 0; i <= str1[0]; ++i) {
string[i] = str1[i];
}
// 未截断
if (str1[0] + str2[0] <= MAX_STR_LENGTH) {
for (int i = 1; i <= str2[0]; ++i) {
string[str1[0] + i] = str2[i];
}
string[0] = str1[0] + str2[0];
return TRUE;
}
// 截断
for (int i = 1; i <= MAX_STR_LENGTH - str1[0]; ++i) {
string[str1[0] + i] = str2[i];
}
string[0] = MAX_STR_LENGTH;
return FALSE;
}
/*
算法 4.3
前置条件:sub 存在
操作结果:用 sub 返回串 str 的第 pos 个字符起长度为 len 的子串
*/
Status SubString(const SqString str, int pos, int len, SqString sub)
{
if ((pos < 1) || (pos > str[0]) || (len < 0) || (len > str[0] - pos + 1)) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return ERR_PARA_ILLEGAL;
}
sub[0] = len;
for (int i = 1; i <= len; ++i) {
sub[i] = str[pos + i - 1];
}
return RET_OK;
}
/*
算法 4.5
前置条件:sub 存在
操作结果:返回子串 string 在主串 str 中第 pos 个字符之后的位置,
最好时间复杂度 O(m + n), 最坏时间复杂度 O(m * n)
若不存在,则函数值为 0 其中,string 非空,1 ≤ pos ≤ StrLength(str)
*/
int Index(const SqString mainStr, const SqString subStr, int pos)
{
if ((pos < 1) || pos > mainStr[0]) {
ERR_RECORD(ERR_PARA_ILLEGAL, "pos = %d", pos);
return 0;
}
int i = pos, j = 1;
while ((i <= mainStr[0]) && (j <= subStr[0])) {
if (mainStr[i] == subStr[j]) {
++i;
++j;
} else {
i = i - j + 2;
j = 1;
}
}
return (j > subStr[0]) ? (i - subStr[0]) : 0;
}
/*
前置条件:串 str 和 string 存在,1 ≤ pos ≤ StrLength(str) + 1
操作结果:在串 str 的第 pos 个字符之前插入串 string
完全插入返回 TRUE,部分插入返回 FALSE
*/
Status StrInsert(const SqString string, int pos, SqString str, Boolean *isFullInsert)
{
if ((pos < 1) || (pos > str[0] + 1)) {
ERR_RECORD(ERR_PARA_ILLEGAL, "pos = %d", pos);
return ERR_PARA_ILLEGAL;
}
*isFullInsert = TRUE;
int offset = string[0];
int length = str[0];
if (str[0] + string[0] <= MAX_STR_LENGTH) {
length += string[0];
} else {
length = MAX_STR_LENGTH;
offset = MAX_STR_LENGTH - str[0];
*isFullInsert = FALSE;
}
// 为插入腾出空间
for (int i = str[0]; i >= pos; --i) {
str[i + offset] = str[i];
}
// 插入
for (int i = pos; i < pos + offset; ++i) {
str[i] = string[i - pos + 1];
}
str[0] = length;
return RET_OK;
}
/*
前置条件:串 str 存在, 1 ≤ pos ≤ StrLength(str) - len + 1
操作结果:从串 str 中删除第 pos 个字符起长度为 len 的子串
*/
Status StrDelete(int pos, int len, SqString str)
{
if ((pos < 1) || (pos > str[0] - len + 1) || (len < 0)) {
ERR_RECORD(ERR_PARA_ILLEGAL, "pos = %d, len = %d", pos, len);
return ERR_PARA_ILLEGAL;
}
for (int i = pos + len; i <= str[0]; ++i) {
str[i - len] = str[i];
}
str[0] -= len;
return RET_OK;
}
/*
前置条件:串 str, string 和 strReplace 存在, string 是非空串(此函数与串的存储结构无关)
操作结果:用 strReplace 替换主串 str 中出现的所有与 string 相等的不重叠的子串
*/
Status Replace(const SqString string, const SqString strReplace, SqString str)
{
Boolean isEmpty;
StrEmpty(string, &isEmpty);
if (isEmpty) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return ERR_PARA_ILLEGAL;
}
Boolean isFullInsert;
Status ret;
int i = 1;
do {
i = Index(str, string, i);
if (i) {
ret = StrDelete(i, StrLength(string), str);
ret |= StrInsert(strReplace, i, str, &isFullInsert);
if (ret != RET_OK) {
ERR_RECORD(ret);
return ret;
}
i += StrLength(strReplace);
}
} while (i);
return RET_OK;
}
/*
前置条件:无
操作结果:由于 SqString 是定长类型,无法销毁
*/
void DestoryString(void)
{
printf("Do not need to destory!\n");
}
/*
前置条件:无
操作结果:输出字符串 string
*/
void StrPrint(SqString string)
{
for (int i = 1; i <= string[0]; ++i) {
printf("%c", string[i]);
}
}
4) algorithm.h
// 算法实现头文件
#ifndef ALGORITHM_H
#define ALGORITHM_H
#include "sqString.h"
int IndexKMP(const SqString mainStr, const SqString tempStr, int pos, const int *next, int nextLength);
Status GetNextArray(const SqString tempStr, int nextLength, int *next);
Status GetNextVal(const SqString tempStr, int nextLength, int *nextVal);
#endif // !ALGORITHM_H
5) algorithm.c
// 算法实现源文件
#include "algorithm.h"
/*
算法 4.6
时间复杂度: O(m + n)
前置条件:tempStr 非空, 1 ≤ pos ≤ StrLength(mainStr)
操作结果:利用模式串 tempStr 的 next 函数求 tempStr 在主串 mainStr 中第 pos 个字符之后的位置的 KMP 算法
*/
int IndexKMP(const SqString mainStr, const SqString tempStr, int pos, const int *next, int nextLength)
{
if (!next) {
ERR_RECORD(ERR_NULL_PTR);
return 0;
}
int maxRange = StrLength(mainStr) - StrLength(tempStr) + 1;
if ((pos < 1) || (pos > maxRange)) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return 0;
}
if (StrLength(tempStr) + 1 != nextLength) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return 0;
}
int tempPos = 1;
while ((pos <= mainStr[0]) && (tempPos <= tempStr[0])) {
if ((tempPos == 0) || (mainStr[pos] == tempStr[tempPos])) {
++pos;
++tempPos;
} else {
tempPos = next[tempPos];
}
}
return (tempPos > tempStr[0]) ? (pos - tempStr[0]) : 0;
}
/*
算法 4.7
时间复杂度: O(m)
前置条件:tempStr 非空
操作结果:求模式串 tempStr 的 next 函数值并存入数组 next
*/
Status GetNextArray(const SqString tempStr, int nextLength, int *next)
{
if (!next) {
ERR_RECORD(ERR_NULL_PTR);
return ERR_NULL_PTR;
}
if (StrLength(tempStr) + 1 != nextLength) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return ERR_PARA_ILLEGAL;
}
next[1] = 0;
int i = 1, j = 0;
while (i < tempStr[0]) {
if ((j == 0) || (tempStr[i] == tempStr[j])) {
++i;
++j;
next[i] = j;
} else {
j = next[j];
}
}
return RET_OK;
}
/*
算法 4.8
时间复杂度: O(m)
前置条件:tempStr 非空
操作结果:求模式串 tempStr 的 next 函数修正值并存入数组 nextval
*/
Status GetNextVal(const SqString tempStr, int nextLength, int *nextVal)
{
if (!nextVal) {
ERR_RECORD(ERR_NULL_PTR);
return ERR_NULL_PTR;
}
if (StrLength(tempStr) + 1 != nextLength) {
ERR_RECORD(ERR_PARA_ILLEGAL);
return ERR_PARA_ILLEGAL;
}
nextVal[1] = 0;
int i = 1, j = 0;
while (i < tempStr[0]) {
if ((j == 0) || (tempStr[i] == tempStr[j])) {
++i;
++j;
if (tempStr[i] != tempStr[j]) {
nextVal[i] = j;
} else {
nextVal[i] = nextVal[j];
}
} else {
j = nextVal[j];
}
}
return RET_OK;
}
6) main.c
// 入口程序源文件
#include "algorithm.h"
#include <stdlib.h>
int main(void)
{
printf("Please input string str1: ");
char str[MAX_STR_LENGTH + 1] = { 0 };
gets_s(str, sizeof(str));
SqString str1 = { 0 };
Status ret = StrAssign(str, str1);
if (ret != RET_OK) {
ERR_RECORD(ret);
return ret;
}
Boolean isEmpty;
StrEmpty(str1, &isEmpty);
printf("The string str1 is %s, the length of str1 is %d\n", (isEmpty == TRUE) ? "empty" : "not empty",
StrLength(str1));
SqString str2 = { 0 };
StrCopy(str1, str2);
printf("The string of str2 copied by str1 is: ");
StrPrint(str2);
printf("\n");
printf("Please input string str2: ");
gets_s(str, sizeof(str));
StrAssign(str, str2);
ret = StrCompare(str1, str2);
char ch = (ret == 0) ? '=' : ((ret < 0) ? '<' : '>');
printf("string str1 %c string str2\n", ch);
SqString t = { 0 };
ret = Concat(str1, str2, t);
printf("The concat of str1 and str2 t is: ");
StrPrint(t);
printf("\n");
if (ret == FALSE) {
printf("There is truncation in connection\n");
}
ClearString(str1);
printf("After clear string str1, str1 is: ");
StrPrint(str1);
printf("\n");
StrEmpty(str1, &isEmpty);
printf("The string str1 is %s, the length of str1 is %d\n", (isEmpty == TRUE) ? "empty" : "not empty",
StrLength(str1));
printf("Please input the start position and length of substring of sting t: ");
int pos, length;
scanf_s("%d%d", &pos, &length);
ret = SubString(t, pos, length, str2);
if (ret == RET_OK) {
printf("The substring of string t str2 is: ");
StrPrint(str2);
printf("\n");
}
printf("Please input the start position and length of substring of sting t to be deleted: ");
scanf_s("%d%d", &pos, &length);
StrDelete(pos, length, t);
printf("After delete substring, the string t is: ");
StrPrint(t);
printf("\n");
pos = StrLength(str2) / 2;
Boolean isFullInsert;
StrInsert(t, pos, str2, &isFullInsert);
printf("After insert string t before the %dth character of string str2, str2 is: ", pos);
StrPrint(str2);
printf("\n");
pos = Index(str2, t, 1);
printf("The %dth character of string str2 is matched with string t\n", pos);
SubString(str2, 1, 1, t);
printf("Substring t is: ");
StrPrint(t);
printf("\n");
Concat(t, t, str1);
printf("The string str1 is: ");
StrPrint(str1);
printf("\n");
Replace(t, str1, str2);
printf("After replace the same string of string t in string str2 that not "
"overload with string str1, string str2 is : ");
StrPrint(str2);
printf("\n");
DestoryString();
/* Algorithm Test */
ClearString(str1);
ClearString(str2);
StrAssign("aaabaaaab", str1);
StrPrint(str1);
printf("\n");
StrAssign("aaaab", str2);
StrPrint(str2);
printf("\n");
length = StrLength(str2);
int *next = (int *)malloc(sizeof(int) * (unsigned long long)(length + 1));
if (!next) {
ERR_RECORD(ERR_MEMORY_ALLOCATE);
return ERR_MEMORY_ALLOCATE;
}
GetNextVal(str2, length + 1, next);
printf("The next array of str2 is:\n");
for (int i = 1; i < length + 1; ++i) {
printf("next[%d] = %d\n", i, next[i]);
}
pos = IndexKMP(str1, str2, 1, next, length + 1);
if (pos) {
printf("The position is %d\n", pos);
} else {
printf("Match failed!\n");
}
free(next);
return 0;
}
3. 输出示例(DEBUG OFF)
1)测试输入
abcdefgh
hijklmn0
2 6
2 1
2)测试输出