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

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

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

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

搜索了网络,发现有两种高效的字符串匹配算法: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;
}
相关推荐
杨哥带你写代码1 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries2 小时前
读《show your work》的一点感悟
后端
A尘埃2 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23072 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code2 小时前
(Django)初步使用
后端·python·django
代码之光_19802 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端
编程老船长2 小时前
第26章 Java操作Mongodb实现数据持久化
数据库·后端·mongodb
IT果果日记3 小时前
DataX+Crontab实现多任务顺序定时同步
后端
姜学迁4 小时前
Rust-枚举
开发语言·后端·rust
爱学习的小健4 小时前
MQTT--Java整合EMQX
后端