在长字符串中判断短字符串的存在

工作中遇到一个需求,需要对一个词典列表(本质是一个字符串列表)进行去除冗余处理。对于没有使用到的词典进行去除。

判断是否使用的规则是,在一个大型数据结构中词典名是否出现。这个数据结构有许多字段,有的字段可能还是嵌套的数据结构,每一层中都有词典出现的可能。

对数据结构进行循环遍历非常麻烦,时间复杂度也很高。想到可以把数据结构进行序列化,转换成判断短字符串是否在长字符串中出现的问题。

搜索了网络,发现有两种高效的字符串匹配算法:KMP算法与Trie字典树。

KMP算法

KMP算法(Knuth-Morris-Pratt算法)是一种高效的字符串匹配算法。它通过构建一个部分匹配表,避免了不必要的回溯,从而提高了匹配的效率。KMP算法的主要思想是利用已匹配的信息,通过移动模式串的方式来避免重新匹配已经匹配过的字符。

KMP算法的优点在于适用于单模式串匹配,对于处理大文本串和多次匹配的情况下具有较好的性能。它的时间复杂度为O(n+m),其中n是长字符串的长度,m是短字符串的长度。KMP算法在实际应用中被广泛使用,例如在文本编辑器中的搜索功能和字符串匹配问题中。

Trie字典树

Trie字典树是一种多模式匹配的数据结构,用于高效地处理多个模式串的匹配问题。它将模式串逐个字符地插入到树中,形成一棵以字符为边的树形结构。通过遍历树,可以判断某个字符串是否存在于字典树中。

Trie字典树的优点在于可以在较短的时间内处理多个模式串的匹配,并且可以方便地进行前缀匹配和模式串的查找。它的时间复杂度为O(m),其中m是待匹配字符串的长度。Trie字典树在搜索引擎、拼写检查和自动补全等领域得到广泛应用。

然而,Trie字典树的缺点是占用较多的内存空间。每个字符都需要一个指针来指向下一个节点,当模式串较长时,字典树的节点数量会急剧增加,从而占用大量的空间。此外,在构建字典树时,需要遍历所有的模式串,构建过程的时间复杂度较高。

总结与应用场景

如果只需要进行单字符串匹配,并且处理大文本串和多次匹配,那么KMP算法是一个不错的选择。它能够快速定位匹配位置,提高匹配效率。

如果需要同时匹配多个不同的字符串,并且希望能够高效地处理多个字符串的匹配,那么Trie字典树是更合适的算法。它能够方便地进行前缀匹配和模式串的查找,适用于搜索引擎、拼写检查和自动补全等领域。

在实际应用中,KMP算法通常比字典树更快速,尤其是当短字符串的数量较少且长度较短时。字典树的优势在于它可以高效地处理大量的短字符串查找,而不需要重复构建前缀函数表。因此,如果需要频繁地对多个短字符串进行查找操作,且这些短字符串会被多次使用,那么字典树可能会更适合。

那我们的场景是不会对字符串进行重复匹配,那么使用 KMP 可能更合适。

使用 KMP 算法在长字符串中查询短字符串的代码:

java 复制代码
/**
* 查看长字符串是否包含短字符串
*/
public static boolean containsSubstring(String longString, String shortString) {
	int n = longString.length();
	int m = shortString.length();

	int[] lps = computeLPSArray(shortString);

	int indexForLong = 0;
	int indexForShort = 0;

	while (indexForLong < n) {
	    if (longString.charAt(indexForLong) == shortString.charAt(indexForShort)) {
	        indexForLong++;
	        indexForShort++;

	        if (indexForShort == m) {
	            return true;
	        }
	    } else {
	        if (indexForShort != 0) {
	            indexForShort = lps[indexForShort - 1];
	        } else {
	            indexForLong++;
	        }
	    }
	}

	return false;
}

/**
* 计算字符串的前缀函数表
*/
private static int[] computeLPSArray(String pattern) {
	int m = pattern.length();
	int[] lps = new int[m];

	int len = 0;
	int i = 1;

	while (i < m) {
	    if (pattern.charAt(i) == pattern.charAt(len)) {
	        len++;
	        lps[i] = len;
	        i++;
	    } else {
	        if (len != 0) {
	            len = lps[len - 1];
	        } else {
	            lps[i] = 0;
	            i++;
	        }
	    }
	}

	return lps;
}
相关推荐
gjxDaniel1 小时前
Go编程语言入门与常见问题
开发语言·后端·go
sunnyday04263 小时前
Spring Boot 自定义 Starter 实战:从创建到使用的完整指南
spring boot·后端·mybatis
想用offer打牌3 小时前
2025年总结:一个树苗倔强生长
java·后端·开源·go
小北方城市网3 小时前
Redis 分布式锁与缓存三大问题解决方案
spring boot·redis·分布式·后端·缓存·wpf·mybatis
哪里不会点哪里.4 小时前
Spring 核心原理解析:它到底解决了什么问题?
java·后端·spring
小杍随笔4 小时前
【Rust Cargo 目录迁移到 D 盘:不改变安装路径和环境变量的终极方案】
开发语言·后端·rust
沐雨风栉5 小时前
用 Kavita+cpolar 把数字书房装进口袋
服务器·开发语言·数据库·后端·golang
郑州光合科技余经理6 小时前
同城020系统架构实战:中台化设计与部署
java·大数据·开发语言·后端·系统架构·uni-app·php
Mr -老鬼6 小时前
UpdateEC - EasyClick 项目热更新系统(Rust构建)
开发语言·后端·rust
一 乐7 小时前
学生宿舍管理|基于springboot + vue学生宿舍管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·助农电商系统