【C语言 数据结构】串 - 顺序
创始人
2024-03-15 21:07:27
0

文章目录

  • 串 - 顺序
    • 一、串的表示及实现
      • 1.1 串的概念
      • 1.2 串的初始化
      • 1.3 复制串
      • 1.4 串的判空
      • 1.5 串的比较
      • 1.6 串的长度
      • 1.7 清空串
      • 1.8 串的拼接
      • 1.9 串的截取
      • 1.10 插入子串
      • 1.11 串的打印
    • 二、串的应用
      • 2.1 串的模式匹配 - 基本操作
      • 2.2 串的模式匹配 - BF
      • 2.3 串的模式匹配 - KMP
        • next数组
      • 测试


串 - 顺序

一、串的表示及实现

1.1 串的概念

在这里插入图片描述

(1)串(String):是零个或多个字符组成的有限序列。一般记为: S='a1a2…an' (n≥0)

  • 其中S为串名,用单引号括起来的为串值, n为串的长度。

(2)子串:串中任意个连续的字符组成的子序列称为该串的子串。

(3)主串:包含子串的串相应地称为主串。

  • 子串在主串中的位置通常将字符在串中的序号称为该字符在串中的位置。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示。

(5)空格串:由一个或多个称为空格的特殊字符组成的串,其长度为串中空格字符的个数。

(6)空串:无任何字符组成的串,其串长度为零。

(7)串相等:只有当两个串的长度相等,并且每个对应位置的字符都相等时才相等。

串的基本操作:

#include
#include
#include
#include
#include// 动态存储串
typedef struct {char *str;  // 基址int maxLength;int length; // 串的大小
} DString;// 初始化操作
void StrAssign(DString *S, int max, char *string);// 复制串
void StrCopy(DString *S, DString T);// 串的判空
bool StrEmpty(DString S);// 串比较
int StrCompare(DString S, DString T);// 串的长度
int StrLength(DString S);// 清空串
void ClearString(DString *S);// 串的拼接
void StrConcact(DString *N, DString S, DString T);// 串的截取
void StrSub(DString* sub, DString S, int pos, int len);// 串的匹配
int StrIndex(DString S, DString T, int pos);
int Index(DString S, DString T, int pos);// 插入子串操作
void StrInsert(DString *S, int pos, DString T);// 串的打印
void StrPrint(DString *S);

返回顶部


1.2 串的初始化

/* 初始化操作* DString *S   串* int max      串的最大长度* char *string 待生成的串*/
void StrAssign(DString *S, int max, char *string) {assert(S);  // 断言int i, m;S->length = strlen(string);  // 获取串的长度// 获取串的允许最大长度if (S->length > max) {m = S->length;} else {m = max;}S->maxLength = m;S->str = (char *) malloc(sizeof(char) * (m)); // 分配串的存储空间if (S->str == NULL) exit(-1);// 创建串for (i = 0; i < S->length; i++) {S->str[i] = string[i];}
}

返回顶部


1.3 复制串

/* 复制串*  DString* S  新串*  DString T   待复制的串*/
void StrCopy(DString *S, DString T) {assert(S);  // 断言// 获取字串的长度S->length = T.length;// 分配串的存储空间S->str = (char *) malloc(sizeof(char) * (S->length));if (S->str == NULL) exit(-1);// 复制串for (int i = 0; i < S->length; i++) {S->str[i] = T.str[i];}
}

返回顶部


1.4 串的判空

/* 串的判空* DString S 待判串*/
bool StrEmpty(DString S) {assert(&S); // 断言return S.length == 0;
}

返回顶部


1.5 串的比较

/* 串比较* DString S  串1* DString T  串2
*/
int StrCompare(DString S, DString T) {// 断言assert(&S);assert(&T);int i = 0;for (; i < S.length && i < T.length; i++) {if (S.str[i] != T.str[i]) {return S.str[i] - T.str[i];}}return S.length - T.length;
}

返回顶部


1.6 串的长度

/* 串的长度*   DString S 判长字串*/
int StrLength(DString S) {assert(&S);return S.length;
}

返回顶部


1.7 清空串

/* 清空串
*  DString* S 待清空串*/
void ClearString(DString *S) {assert(S);  // 断言free(S->str);  // 释放空间S->str = NULL; // 置空S->length = 0; // 置零
}

返回顶部


1.8 串的拼接

/* 串的拼接* DString *N  合并的新串* DString S  串1* DString T  串2*/
void StrConcact(DString *N, DString S, DString T) {assert(&S);assert(&T);N->length = S.length + T.length; // 新串的长度N->str = (char *) malloc(sizeof(char) * (N->length));// 分配串的存储空间if (N->str == NULL) exit(-1);// 串1for (int i = 0; i < S.length; i++) {N->str[i] = S.str[i];}// 串2for (int i = 0; i < N->length; i++) {N->str[i + S.length] = T.str[i];}
}	

返回顶部


1.9 串的截取

/* 串的截取*  DString *sub  截取的新串*  DString S 被截取的母串*  int pos 截取子串的开始位置*  int len 截取子串的长度*/
void StrSub(DString *sub, DString S, int pos, int len) {assert(&S); // 断言sub->length = len;  // 新串的长度sub->str = (char *) malloc(sizeof(char) * (sub->length + 1)); //分配串的存储空间if (sub->str == NULL) exit(-1);if (pos >= 0 && pos < S.length && len >= 0 && len <= S.length - pos + 1) {for (int i = 0; i < len; i++) { // 遍历sub->str[i] = S.str[pos + i];  // 从pos位置开始取字符}} else {printf("参数有问题,请核实!");exit(-1);}
}

返回顶部


1.10 插入子串

/* 插入子串操作* DString *S 串* int pos    插入子串的起始位置* DString T  待插入的串*/
void StrInsert(DString *S, int pos, DString T) {assert(S);  // 断言int i;       // 变量ichar *p;  // 新的基址if (pos < 0) {printf("参数pos出错!");exit(-1);} else {// 如果插入的串和原串的长度大于初始定义的最大串长if (S->length + T.length > S->maxLength) {// 重新分配空间p = (char *) realloc(S->str, (S->length + T.length) * sizeof(char));}// 原串从插入位置开始的串向后移动新串的长度个位置for (i = S->length - 1; i >= pos; i--) {S->str[i + T.length] = S->str[i];}// 子串插入for (i = 0; i < T.length; i++) {S->str[pos + i] = T.str[i];}// 新串的长度 = 原串 + 子串S->length = S->length + T.length;}
}

返回顶部


1.11 串的打印

/* 串的打印* DString *S 串*/
void StrPrint(DString *S) {assert(S);  // 断言int i;for (i = 0; i < S->length; i++) {printf("%c", S->str[i]);}printf("\n");
}

返回顶部


二、串的应用

模式匹配:求子串(模式串)在主串(目标串)中的位置,若子串出现在主串中,则匹配成功,否则匹配不成功。常应用于文章中关键字的查找。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgtBLS5z-1670080162200)(https://test1.jsdelivr.net/gh/Code-for-dream/Blogimages/img/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/image-20221125031103045.png)]

2.1 串的模式匹配 - 基本操作

/* 串的匹配* DString S 母串* DString T 字串* int pos 位置*/
int StrIndex(DString S, DString T, int pos) {int n, m, i;DString sub;if (pos > 0) {n = StrLength(S);// 得到主串S的长度  17m = StrLength(T);// 得到子串T的长度   8i = pos;while (i <= n - m + 1) {  // 17-8+1 = 10StrSub(&sub, S, i, m);    // 取主串第i个位置,长度与T相等子串给subStrPrint(&sub);if (StrCompare(sub, T) != 0) ++i;// 如果两串不相等else return i;  // 如果两串相等}}return 0;    // 若无子串与T相等,返回0
}

返回顶部


2.2 串的模式匹配 - BF

在这里插入图片描述

简单的模式匹配:iii 和 jjj 分别指向主串 SSS 中的字符 和 子串TTT 中的字符,如果Si=TjS_i=T_jSi​=Tj​,i4i4i4 和 jjj 分别加 111,指向下一个字符,否则,iii 退回到本趟匹配起始位置的下一个位置,jjj 重新指向子串的第一个字符,开始下一趟匹配。

/** BF 算法* DString S  母串* DString T  子串*/
int Index(DString S, DString T, int pos) {if (pos < 0 || pos > S.length - T.length) {printf("pos参数值有误,请校验!");exit(-1);}int i = pos;               // i用于主串S中当前位置下标值int j = 0;                 // j用于子串T中当前位置下标值while (i < S.length && j < T.length) {  // 若i小于S的长度并且j小于T的长度时,循环继续if (S.str[i] == T.str[j]) {         // 两字母相等则继续i++;j++;} else {               // 指针后退重新开始匹配i = i - j + 1;     // i退回到上次匹配首位的下一位,即i=i-j+1j = 0;             // j退回到子串T的首位}}//子串所有字符都匹配完毕if (j >= T.length) {return (i - j); // 返回匹配的第一个字符的下标} else {return 0;      // 匹配失败}
}

缺点:存在回溯的问题!

返回顶部


2.3 串的模式匹配 - KMP

KMP算法核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数,从而达到快速匹配的目的。

KMP算法与BF算法(暴力算法)区别在于,主串的 iii 不会回退,并且模式串的 jjj 不会每次都回到 0 位置。

image-20221125044725773

假设此时 i 指向 g,j 指向 c,匹配失败。

  • 此时 i 不进行回退,因为在这个地方前,两个字符串是有一部分相同的。
  • 我们观察,当我们保持 i 不动, j 退回到第三个位置也就是 c 时,不难发现两个字符串前 a b 是一样的。
  • 因此,我们需要借助 next 数组,帮助我们找到 j 需要回退的位置。

next数组

  • KMP算法的精髓就是next数组,next[j]=k,表示不同的 j 对应一个 k 值;k 表示模式串下标为 j 的元素,匹配失败时,要退回的位置。

  • 最长前后相等子串

    image-20221125032841992
  • next[j] = 匹配字符个数 + 1 参考视频:三分钟搞定 数据结构 串 KMP算法next数组求值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0L8zo14-1670080162201)(https://test1.jsdelivr.net/gh/Code-for-dream/Blogimages/img/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/image-20221125043152387.png)]

/** KPM*/
void Next(char *T, int *next) {next[1] = 0;  // 初始化默认值int i = 1;int j = 0;while (i < strlen(T)) {if (j == 0 || T[i - 1] == T[j - 1]) {i++;j++;next[i] = j;} else {j = next[j];}}
}int KMP(char *S, char *T) {int next[10];Next(T, next);//根据模式串T,初始化next数组int i = 1;int j = 1;while (i <= strlen(S) && j <= strlen(T)) {//j==0:代表模式串的第一个字符就和当前测试的字符不相等;// S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移if (j == 0 || S[i - 1] == T[j - 1]) {i++;j++;} else {j = next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值}}if (j > strlen(T)) {//如果条件为真,说明匹配成功return i - (int) strlen(T);}return -1;
}

返回顶部


测试

#include "Strand.c"void TestDString() {// 创建串Sprintf("*******************\n");printf("S串的创建:");DString S;StrAssign(&S, 5, "abc");// 打印串SStrPrint(&S); // abc// 新的串Tprintf("*******************\n");printf("S串的插入 => S+T:");DString T;StrAssign(&T, 3, "abc");// 将新串T插入S,从下标2开始StrInsert(&S, 3, T);// 打印串SStrPrint(&S); // abcabc// 复制串printf("*******************\n");printf("S串的复制 => P:");DString P;StrCopy(&P, S);StrPrint(&P); // abcabc// 串的比较printf("*******************\n");printf("T、P串的比较:");int res = StrCompare(T, P);printf("%d\n", res);  // -1// 串的长度printf("*******************\n");printf("S串的长度是:%d\n", StrLength(S)); // 6// 合并串printf("*******************\n");printf("S、P串的合并:");DString *newStr;StrConcact(&newStr, S, P);StrPrint(&newStr); // abcabc abcabc// 串的截取printf("*******************\n");printf("S串的截取:");DString newStr1;StrSub(&newStr1, S, 1, 5); // abcabcStrPrint(&newStr1);// 串的匹配printf("*******************\n");printf("串的匹配:\n");char a1[] = "acabaabcaabaabcac";char a2[] = "abaabcac";DString a;StrAssign(&a, 17, a1);DString b;StrAssign(&b, 8, a2);// 打印串SStrPrint(&a);StrPrint(&b);int result1 = StrIndex(a, b, 1);int result2 = Index(a, b, 1);printf("BF算法匹配字串的位置:%d\n", result1);printf("BF算法匹配字串的位置:%d\n", result2);
}int main() {TestDString();int next[100];char s1[] = "acabaabcaabaabcac";char s2[] = "abaabcac";int i = KMP(s1, s2);printf("%d\n", i);system("pause");return 0;}

在这里插入图片描述

返回顶部


相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...