当前位置: 首页 > news >正文

字符串算法

目录

1. 引言

2. 基础概念

3. 字符串匹配算法

朴素匹配算法

KMP(Knuth-Morris-Pratt)算法

Boyer-Moore算法


1. 引言

简述字符串算法的重要性 字符串是计算机科学中的基本数据结构,广泛应用于文本处理、数据分析、网络安全等领域。有效的字符串算法可以极大地提高这些应用的效率和性能,因此对这些算法的理解和掌握至关重要。

常见的字符串处理任务

  • 匹配:查找一个字符串是否存在于另一个字符串中。
  • 搜索:在大量字符串中快速找到目标字符串。
  • 排序:根据字典序或其他规则对字符串数组进行排序。
  • 压缩:减少字符串的存储空间。

2. 基础概念

字符串的定义与表示 字符串是由字符组成的有限序列,通常用于表示文本数据。字符串可以用不同的编码方式进行存储和处理,如ASCII和Unicode。

ASCII码与Unicode

  • ASCII码:采用7位或8位二进制编码,用于表示英文字符及常用符号。
  • Unicode:一种通用的字符编码标准,可以表示几乎所有语言的字符,常用的编码方式包括UTF-8、UTF-16等。

字符串的基本操作

  • 连接:将两个或多个字符串拼接成一个新字符串。
  • 截取:从字符串中提取一个子字符串。
  • 查找:寻找某个子字符串在字符串中的位置。
  • 替换:将字符串中的某个子字符串替换为另一个字符串。

3. 字符串匹配算法

朴素匹配算法

算法原理

朴素匹配算法(Naive String Matching Algorithm)是一种最简单的字符串匹配算法,其核心思想是逐个字符地比较目标字符串(Text)中的每一个子串与模式字符串(Pattern),直到找到匹配的子串或遍历完整个目标字符串。

算法步骤

  1. 将目标字符串的第一个字符与模式字符串的第一个字符进行比较。
  2. 如果匹配,继续比较下一个字符;如果不匹配,从目标字符串的下一个字符开始重新比较。
  3. 重复上述步骤,直到找到匹配或遍历完整个目标字符串。

代码示例

python语言:

def naive_string_matcher(text, pattern):n = len(text)m = len(pattern)for i in range(n - m + 1):match = Truefor j in range(m):if text[i + j] != pattern[j]:match = Falsebreakif match:print(f"Pattern found at index {i}")# 示例使用
text = "ABCDEFABCDEF"
pattern = "CDE"
naive_string_matcher(text, pattern)

C语言:

# include <stdio.h>
# include <stdlib.h>typedef struct String {char *data;int len;
}String;String *initString(){String *s = (String *)malloc(sizeof(String));s->data = NULL;s->len = 0;return s;
}void stringAssign(String *s, char *data){// 释放字符串结构体中的数据内存if (s -> data) {free(s -> data);}int len = 0;// 将指针 data 的值赋给指针 temp,使得 temp 指向 data 所指向的内存地址。char* temp = data;// 通过遍历字符串中的每个字符,直到遇到字符串结束符'\0',//每遍历一个字符,长度计数器len就增加1。while (*temp) {len++;temp++;}if (len == 0){s -> data = NULL;s -> len = 0;}else{temp = data;s -> len = len;s -> data = (char *)malloc((len + 1) * sizeof(char));for (int i = 0; i <= len; i++,temp++){s -> data[i] = *temp;}}
}void printString(String *s){// 打印字符串的函数,接受一个指向String结构体的指针作为参数// 遍历字符串中的每个字符,使用箭头连接并打印出来for (int i = 0; i < s -> len; i++){printf(i == 0 ? "%c " : "-> %c", s -> data[i]);}printf("\n");
}void forceMatch(String *master, String *sub){// 字符串匹配的暴算法,接受两个指向String结构体的指针作为参数int i = 0,j = 0;while (i < master -> len && j < sub -> len){    if (master -> data[i] == sub -> data[j]){i++;j++;}else{// 匹配失败,重置i(回到起点的下一位),j,继续匹配i = i - j + 1;j = 0;}}if (j == sub -> len){printf("force Match success!\n");}else{printf("force Match fail!\n");}
}int main(){String *s1 = initString();String *s2 = initString();stringAssign(s1, "hello world");stringAssign(s2, "world");printString(s1);printString(s2);forceMatch(s1, s2);return 0;
}

输出结果

Pattern found at index 2
Pattern found at index 8

时间复杂度分析 朴素匹配算法的时间复杂度取决于目标字符串的长度 n 和模式字符串的长度 m

  • 最坏情况:O((n - m + 1) * m),即在每个位置都进行完全比较。
  • 最优情况:O(n),即模式字符串在目标字符串的开头找到。

案例分析 考虑一个实际应用场景,比如在一个长文章中搜索某个特定的词语。在这样的场景下,朴素匹配算法虽然简单,但在大多数情况下效率较低,尤其是当目标字符串和模式字符串都很长时。

图解说明 可以通过图示展示朴素匹配算法的逐步匹配过程。在下图中,每行代表一次新的比较,绿色表示成功匹配的字符,红色表示匹配失败后重新开始。

总结 朴素匹配算法虽然易于理解和实现,但在处理大型数据集时性能不佳。因此,通常会使用更为复杂和高效的算法,如KMP算法或Boyer-Moore算法。

KMP(Knuth-Morris-Pratt)算法

算法背景与概述 KMP算法是由Donald Knuth、Vaughan Pratt和James H. Morris在1977年提出的一种字符串匹配算法。KMP算法通过预处理模式字符串来避免在匹配过程中对目标字符串的字符进行重复比较,从而提高匹配效率。

部分匹配表(前缀表)的构建 KMP算法的核心在于部分匹配表(又称为前缀表)的构建。该表用于记录模式字符串中每个位置的最长相同前缀与后缀的长度,从而在模式字符串出现部分匹配时,能够快速跳转至下一个可能匹配的位置。

构建步骤

  1. 初始化前缀表,长度为模式字符串的长度。
  2. 遍历模式字符串,对于每个位置计算最长相同前缀与后缀的长度,并存入前缀表中。

代码示例:前缀表构建

def build_prefix_table(pattern):m = len(pattern)prefix_table = [0] * mj = 0  # length of the previous longest prefix suffixi = 1while i < m:if pattern[i] == pattern[j]:j += 1prefix_table[i] = ji += 1else:if j != 0:j = prefix_table[j - 1]else:prefix_table[i] = 0i += 1return prefix_table# 示例使用
pattern = "ABABCABAB"
prefix_table = build_prefix_table(pattern)
print("Prefix Table:", prefix_table)

输出结果

Prefix Table: [0, 0, 1, 2, 0, 1, 2, 3, 2]

算法步骤

  1. 使用前缀表进行模式匹配,比较目标字符串与模式字符串。
  2. 当出现部分匹配时,利用前缀表跳过一些无用的比较。
  3. 重复上述过程,直到找到匹配或遍历完整个目标字符串。

代码示例:KMP算法

python语言:

def KMP_search(text, pattern):n = len(text)m = len(pattern)prefix_table = build_prefix_table(pattern)i = 0  # index for textj = 0  # index for patternwhile i < n:if pattern[j] == text[i]:i += 1j += 1if j == m:print(f"Pattern found at index {i - j}")j = prefix_table[j - 1]elif i < n and pattern[j] != text[i]:if j != 0:j = prefix_table[j - 1]else:i += 1# 示例使用
text = "ABABDABACDABABCABAB"
pattern = "ABABCABAB"
KMP_search(text, pattern)

C语言:

#include <stdio.h>
#include <stdlib.h>typedef struct String
{char *data;int len;
} String;String *initString()
{String *s = (String *)malloc(sizeof(String));s->data = NULL;s->len = 0;return s;
}void stringAssign(String *s, char *data)
{if (s->data){free(s->data);}int len = 0;char *temp = data;while (*temp){len++;temp++;}if (len == 0){s->data = NULL;s->len = 0;}else{temp = data;s->len = len;s->data = (char *)malloc(sizeof(char) * (len + 1));for (int i = 0; i < len; i++){s->data[i] = *temp;temp++;}}
}void printString(String *s)
{if (s->data){for (int i = 0; i < s->len; i++){printf(i == 0 ? "%c" : "-> %c", s->data[i]);}}printf("\n");
}int *getNext(String *s)
{int *next = (int *)malloc(sizeof(int) * s->len);int i = 0, j = -1;next[0] = -1;while (i < s->len - 1){if (j == -1 || s->data[i] == s->data[j]){i++;j++;next[i] = j;}else{j = next[j];}}return next;
}void printNext(int *next, int len)
{for (int i = 0; i < len; i++){printf(i == 0 ? "%d" : "-> %d", next[i]);}printf("\n");
}void kmpMatch(String *master, String *sub, int* next){// 函数kmpMatch用于实现KMP字符串匹配算法// 参数master: 主字符串// 参数sub: 子字符串// 参数next: next数组,用于存储子字符串的最长公共前后缀长度int i = 0,j = 0;while (i < master->len && j < sub->len){if (j == -1 || master -> data[i] == sub -> data[j]){i++;j++;}else {j = next[j];}}if (j == sub->len){printf(" kmp match success. \n");}else{printf("kmp match fail. \n");}
}      int main()
{String *s1 = initString();String *s2 = initString();stringAssign(s1, "hello world");stringAssign(s2, "world");printString(s1);int *next = getNext(s2);printNext(next, s2->len);kmpMatch(s1, s2, next);return 0;
}

输出结果

Pattern found at index 10

时间复杂度与空间复杂度分析

  • 时间复杂度:KMP算法的时间复杂度为O(n + m),其中n为目标字符串的长度,m为模式字符串的长度。这是因为KMP算法通过前缀表避免了无用的重复比较,使得每个字符最多被比较两次。
  • 空间复杂度:前缀表的构建需要额外的O(m)空间,因此总的空间复杂度也是O(m)。

优缺点及应用场景

  • 优点
    • KMP算法在匹配过程中不需要回溯,提高了匹配效率。
    • 适用于大规模文本搜索以及对时间复杂度要求较高的应用场景。
  • 缺点
    • 前缀表的构建过程较为复杂,尤其是对于初学者而言。
  • 应用场景
    • 文本编辑器中的查找替换功能。
    • DNA序列比对。
    • 网络爬虫中的网页内容查找。

推荐视频:

  • up主:天勤考研--------KMP算法易懂版
  • up主:齐乐编程学院--------- KMP算法讲解(C版)

Boyer-Moore算法

算法特点 Boyer-Moore算法是另一种经典的字符串匹配算法,主要特点是从模式字符串的末尾开始向前匹配。该算法通过两个规则(坏字符规则和好后缀规则)来决定模式字符串的移动步数,从而大幅减少了比较次数。

算法步骤

  1. 坏字符规则:当模式字符串的一个字符与目标字符串的字符不匹配时,直接跳过坏字符在模式字符串中的所有出现位置,或将模式字符串移动到坏字符在模式字符串中最右的位置。
  2. 好后缀规则:当模式字符串的部分后缀与目标字符串的部分匹配时,利用模式字符串中的重复部分来跳过无用的匹配。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • wangeditor编辑器自定义按钮和节点,上传word转换html,文本替换
  • 简单封装一个类似菜单栏的树状结构转换
  • VMware-Ubuntu共享文件找不到
  • 深入探索分布式任务调度框架:MySQL实现高效锁机制
  • 误删?损坏?SD卡数据恢复全攻略,让你的数据起死回生!
  • RK3568平台(PWM篇)PWM驱动
  • xss-靶场
  • 基于协同过滤算法的体育商品推荐系统_t81xg
  • 气膜游泳馆:夏日避暑的绝佳选择—轻空间
  • Perl(Practical Extraction and Reporting Language)脚本
  • 自主身份:Web3如何重新定义个人数据所有权
  • 基于 Spring Boot 的快速开发微信公众平台的框架-FastBootWeixin框架
  • RabbitMQ-消息队列之topic使用
  • Linux目录结构及基础查看命令和命令模式
  • EmguCV学习笔记 VB.Net 4.5 像素距离和连通区域
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Docker容器管理
  • EOS是什么
  • express.js的介绍及使用
  • FineReport中如何实现自动滚屏效果
  • iOS小技巧之UIImagePickerController实现头像选择
  • JS基础之数据类型、对象、原型、原型链、继承
  • Spring Boot MyBatis配置多种数据库
  • tensorflow学习笔记3——MNIST应用篇
  • Vue ES6 Jade Scss Webpack Gulp
  • VUE es6技巧写法(持续更新中~~~)
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 大主子表关联的性能优化方法
  • 前端技术周刊 2019-01-14:客户端存储
  • 入手阿里云新服务器的部署NODE
  • 三分钟教你同步 Visual Studio Code 设置
  • 数据结构java版之冒泡排序及优化
  • 小程序测试方案初探
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • elasticsearch-head插件安装
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (2020)Java后端开发----(面试题和笔试题)
  • (3)llvm ir转换过程
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (pycharm)安装python库函数Matplotlib步骤
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (蓝桥杯每日一题)love
  • (四)js前端开发中设计模式之工厂方法模式
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (转) Android中ViewStub组件使用
  • ***监测系统的构建(chkrootkit )
  • *2 echo、printf、mkdir命令的应用
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .net 程序发生了一个不可捕获的异常
  • .NET 的程序集加载上下文
  • .net 中viewstate的原理和使用
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?