串
串的定义
串是由零个或多个字符组成的有限序列,又叫字符串
串的比较
给定两个串:s="a1a2a3.....an",t="b1b2b3.....bm",当满足以上条件之一时,s<t
1.n<m,且ai=bi(i=1,2...n)
列如当s="hap",t="happy",就有s<t。因为t比s多出了两个字母
2.存在某个k<=min(n,m),使得ai=bi(i=1,2,....,k-1),ak<bk
列如当s="happen",t="happy",因为前四个相等,e<y,所以s<t
串的存储结构
1.串的顺序存储结构
串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。
2.串的链式存储结构
对于串的链式存储结构,与线性表示相似的,但由于串结构的特殊性,结构中的每个元素数据是一个字符,如果也简单地应用链表存储串值,一个结点对应一个字符就会存在很大的空间浪费,因此,一个结点可以存放一个字符,也可存放多个字符。
总体来说,串的链式存储结构除了在连接串与串操作时有一定方便之外,总的来说不如顺序存储灵活,性能也不如顺序存储结构好
朴素的模式匹配算法(BF算法)
判断两个串是否存在子串与主串的关系,最直接的算法就是拿着模式串,去和主串从头到尾一一比对,这就是“BF”算法的实现思想。
#include <stdio.h> #include <string.h> int sel(char * S,char *T){ int i=0,j=0; while (i<strlen(S) && j<strlen(T)) { if (S[i]==T[j]) { i++; j++; }else{ i=i-j+1; j=0; } } //跳出循环有两种可能,i=strlen(S)说明已经遍历完主串;j=strlen(T),说明模式串遍历完成,在主串中成功匹配 if (j==strlen(T)) { return i-strlen(T)+1; } //运行到此,为i==strlen(S)的情况 return 0; } int main() { int add=sel("ababcabcacbab", "abcac"); printf("%d",add); return 0; }
快速模式匹配算法(KMP算法)
普通模式匹配算法,大体思路是:模式串从主串的第一个字符开始匹配,每匹配失败,主串中记录匹配进度的指针 i 都要进行 i-j+1 的回退操作,同时模式串向后移动一个字符的位置。一次次的循环,直到匹配成功或者程序结束。
"KMP"算法相比于"BF"算法,优势在于:
- 在保证指针 i 不回溯的前提下,当匹配失败时,让模式串向右移动最大的距离;
- 并且可以在O(n+m)的时间数量级上完成对串的模式匹配操作;
故,"KMP"算法称为“快速模式匹配算法”。
KMP的难点主要是在next数组,具体推断不太好写,可以网上参考优秀博客异或这B站视频
可以编辑器运行以下代码,把next数组的变化输出。可以方便理解。
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstring> #include<stdio.h> #include<algorithm> #include<map> #include<queue> #include<set> #include <sstream> #include<vector> #include<cmath> #include<stack> #include<time.h> #include<ctime> using namespace std; #define inf 1<<30 #define eps 1e-7 #define LD long double #define LL long long #define maxn 100000005 /* P 为模式串,下标从 0 开始 */ void GetNext(string P, int next[]) { int p_len = P.size(); int i = 0; // P 的下标 int j = -1; next[0] = -1; while (i < p_len) { if (j == -1 || P[i] == P[j]) { i++; j++; next[i] = j; } else j = next[j]; } } /* 在 S 中找到 P 第一次出现的位置 */ int KMP(string S, string P, int next[]) { GetNext(P, next); int i = 0; // S 的下标 int j = 0; // P 的下标 int s_len = S.size(); int p_len = P.size(); while (i < s_len && j < p_len) // 因为末尾 '\0' 的存在,所以不会越界 { if (j == -1 || S[i] == P[j]) // P 的第一个字符不匹配或 S[i] == P[j] { i++; j++; } else { j = next[j]; // 当前字符匹配失败,进行跳转 } } if (j == p_len) // 匹配成功 return i - j; return -1; } int main() { int next[100] = { 0 }; cout << KMP("bbc abcdab abcdabcdabde", "abcdabd", next) << endl; // 15 return 0; }
关于对KMP算法的改进
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstring> #include<stdio.h> #include<algorithm> #include<map> #include<queue> #include<set> #include <sstream> #include<vector> #include<cmath> #include<stack> #include<time.h> #include<ctime> using namespace std; #define inf 1<<30 #define eps 1e-7 #define LD long double #define LL long long #define maxn 100000005 /* P 为模式串,下标从 0 开始 */ void GetNext(string P, int next[]) { int p_len = P.size(); int i = 0; // P 的下标 int j = -1; next[0] = -1; while (i < p_len) { if (j == -1 || P[i] == P[j]) { i++; j++; if (P[i] != P[j]) next[i] = j; else next[i] = next[j]; } else j = next[j]; } } /* 在 S 中找到 P 第一次出现的位置 */ int KMP(string S, string P, int next[]) { GetNext(P, next); int i = 0; // S 的下标 int j = 0; // P 的下标 int s_len = S.size(); int p_len = P.size(); while (i < s_len && j < p_len) // 因为末尾 '\0' 的存在,所以不会越界 { if (j == -1 || S[i] == P[j]) // P 的第一个字符不匹配或 S[i] == P[j] { i++; j++; } else { j = next[j]; // 当前字符匹配失败,进行跳转 } } if (j == p_len) // 匹配成功 return i - j; return -1; } int main() { int next[100] = { 0 }; cout << KMP("bbc abcdab abcdabcdabde", "abcdabd", next) << endl; // 15 return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统