(1)串(String
):是零个或多个字符组成的有限序列。一般记为: S='a1a2…an' (n≥0)
。
(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);
返回顶部
/* 初始化操作* 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];}
}
返回顶部
/* 复制串* 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];}
}
返回顶部
/* 串的判空* DString S 待判串*/
bool StrEmpty(DString S) {assert(&S); // 断言return S.length == 0;
}
返回顶部
/* 串比较* 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;
}
返回顶部
/* 串的长度* DString S 判长字串*/
int StrLength(DString S) {assert(&S);return S.length;
}
返回顶部
/* 清空串
* DString* S 待清空串*/
void ClearString(DString *S) {assert(S); // 断言free(S->str); // 释放空间S->str = NULL; // 置空S->length = 0; // 置零
}
返回顶部
/* 串的拼接* 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];}
}
返回顶部
/* 串的截取* 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);}
}
返回顶部
/* 插入子串操作* 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;}
}
返回顶部
/* 串的打印* DString *S 串*/
void StrPrint(DString *S) {assert(S); // 断言int i;for (i = 0; i < S->length; i++) {printf("%c", S->str[i]);}printf("\n");
}
返回顶部
模式匹配:求子串(模式串)在主串(目标串)中的位置,若子串出现在主串中,则匹配成功,否则匹配不成功。常应用于文章中关键字的查找。
/* 串的匹配* 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
}
返回顶部
简单的模式匹配: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; // 匹配失败}
}
缺点:存在回溯的问题!
返回顶部
KMP算法核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数,从而达到快速匹配的目的。
KMP算法与BF算法(暴力算法)区别在于,主串的 iii 不会回退,并且模式串的 jjj 不会每次都回到 0 位置。
假设此时 i 指向 g,j 指向 c,匹配失败。
KMP算法的精髓就是next数组,next[j]=k,表示不同的 j 对应一个 k 值;k 表示模式串下标为 j 的元素,匹配失败时,要退回的位置。
最长前后相等子串
next[j] = 匹配字符个数 + 1
参考视频:三分钟搞定 数据结构 串 KMP算法next数组求值/** 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;}
返回顶部