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

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

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

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

搜索了网络,发现有两种高效的字符串匹配算法: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;
}
相关推荐
aiopencode4 分钟前
抓包技术全面指南:原理、工具与应用场景
后端
该用户已不存在25 分钟前
Gemini 3.0 发布,Antigravity 掀桌,程序员何去何从?
后端·ai编程·gemini
aiopencode27 分钟前
软件苹果商城上架的流程与团队协作模式 一个项目从开发到发布的完整经历
后端
yeyong27 分钟前
playwright的调试模式,方便调试selector, locator语法及查找效果
后端
鹿里噜哩34 分钟前
Spring Authorization Server 打造认证中心(一)项目搭建/集成
java·后端·spring
汤姆yu1 小时前
基于springboot的智慧家园物业管理系统
java·spring boot·后端
百***69441 小时前
如何使用Spring Boot框架整合Redis:超详细案例教程
spring boot·redis·后端
q***31141 小时前
【Springboot3+vue3】从零到一搭建Springboot3+vue3前后端分离项目之后端环境搭建
android·前端·后端
e***0961 小时前
【Spring】配置文件的使用
java·后端·spring
a***13141 小时前
【spring专题】编译spring5.3源码
java·后端·spring