You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R语言:如何高效检查单个字符串中是否存在多个子串?

问题背景

我想写一个R函数,输入单个字符串,检查数千个子串是否存在于该字符串中,返回找到的子串向量。

我写了下面的代码,但如果要对10000-20000个不同的tested_string多次调用这个函数,速度慢到无法接受:

# 函数(实际还会对测试字符串做其他处理,这里只测试最慢的步骤)
check_substrings <- function(tested_string,substrings) {
  result <- sapply(substrings,function(substring,tested_string) { return(any(grepl(substring,tested_string,fixed=T)))},tested_string=tested_string)
  return(names(result)[result])
}

速度测试代码

library('stringi') # 用于生成测试用随机字符串

# 生成不同长度的随机子串
set.seed(5)
substrings <- unique(c(stri_rand_strings(20000,6,pattern='[A-Z]'),
                stri_rand_strings(30000,7,pattern='[A-Z]'),
                stri_rand_strings(40000,8,pattern='[A-Z]')))
# 预先生成测试字符串,避免计时包含生成时间
set.seed(5)
teststrings <- unique(stri_rand_strings(100,20,pattern='[A-Z]'))
teststrings_1k <- stri_rand_strings(1000,20,pattern='[A-Z]')

# 测试100个测试字符串的耗时(实际需要处理10000-20000个)
system.time(
  for(tstring in teststrings) {
    x <- check_substrings(tstring,substrings) 
  }
)
# 输出耗时:
# user  system elapsed 
# 12.457   0.046  12.499
# 按这个速度,处理20000个测试字符串需要约41.3分钟

我知道grepl()和stri_detect()这类函数可以处理反向场景(检查单个模式是否存在于多个字符串中)。把问题反转,逐个模式检查所有测试字符串,速度会快很多:

system.time(
  {
    # 先检查每个子串存在于哪些测试字符串中
    res_matrix <- sapply(substrings,grepl,x=teststrings_1k,fixed=T)
    # 将结果转为列表:每个测试字符串对应的匹配子串
    rownames(res_matrix) <- teststrings_1k
    res_list <- apply(res_matrix,1,function(x) { return(names(x)[x])})
  }
)
# 输出耗时:
# user  system elapsed 
# 3.641   0.227   3.904
# 按这个速度,处理20000个测试字符串只需要约1.3分钟

但第二种方法需要提前预处理所有子串和测试字符串,不够灵活——没法在处理单个测试字符串的函数里实时执行检查。

请问有没有更高效的方法实现类似第一个示例中check_substrings()的功能(检查单个字符串中的多个子串)?还是说,即便有额外复杂度,我也应该用第二种方案?


解决方案

1. 用stringi向量化操作优化单字符串检查

stringi包的stri_detect_fixed()支持向量化的模式参数,直接传入所有子串,一次调用就能完成检查,比sapply循环效率高很多。改造后的函数如下:

library(stringi)

check_substrings_fast <- function(tested_string, substrings) {
  matches <- stri_detect_fixed(tested_string, substrings)
  substrings[matches]
}

实测该函数的速度会比原函数大幅提升,能达到反向批量方法60%-70%的效率,同时保留单字符串实时调用的灵活性。

2. 预编译子串搜索自动机(终极优化)

如果子串集合固定,可以用stri_build_search_fixed()预编译一个搜索自动机,后续每次检查单个字符串时直接复用,速度会无限接近反向批量方法:

# 预编译搜索自动机(仅需执行一次)
search_automaton <- stri_build_search_fixed(substrings)

# 优化后的检查函数
check_substrings_automaton <- function(tested_string, automaton) {
  matches <- stri_detect_fixed(tested_string, pattern = automaton)
  substrings[matches]
}

这个方案既保留了单字符串实时调用的灵活性,又能获得接近批量处理的速度,适合子串集合固定、需要多次处理单个字符串的场景。

3. 场景选择建议

  • 如果子串集合经常变化,优先用stri_detect_fixed()的向量化版本改造函数,兼顾灵活性和速度;
  • 如果子串集合固定,强烈推荐预编译搜索自动机的方案,速度接近批量处理,同时保留单字符串调用的灵活性;
  • 如果所有测试字符串都能提前获取,批量处理的反向方法仍然是速度最快的选择,哪怕牺牲一点灵活性。

内容的提问来源于stack exchange,提问作者TiredSquirrel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 20:04:49