【发布时间】:2017-05-23 21:22:05
【问题描述】:
我正在寻找一种查找字符串中多次出现的所有单词的好方法。有一些限制:
- 需要亚二次方速度:大约 1000 个字,我可以承受几毫秒。
- 必须在纯 make 中实现:
- 我想避免使用 $(shell),因为它很贵而且必须在 Windows 上运行(在纯 Linux 上,sort|uniq -u 可以很好地解决我的问题)。
- No Guile,因为我无法控制使用的 make 版本,并且需要与旧的 make 版本 (3.81) 兼容。
- 实现的可读性应达到可接受的程度。
此外,重复的数量会很少,单词只会包含漂亮的字符,例如 [-_+a-zA-Z0-9]+。
我尝试了两种策略:
(1) 强制 $(sort) 保留重复项(为每个单词添加唯一的后缀,排序并去除后缀)。然后在排序列表中找到相邻的相同单词:
# given 0 1 0 1 0 1 0 1 ... , return 0 0 1 1 0 0 1 1 ...
double=$(wordlist 1,$(words $(1)),$(subst 0,0 0,$(subst 1,1 1,$(1))))
# Produce a list of N unique strings. $(1) contains N words, with a
# repetition cycle of length M, and $(2) contains N words, either 0 or
# 1, alternating between 0 and 1 every Mth word.
binseq=$(if $(findstring 1,$(2)),$(call binseq,$(join $(2),$(1)),$(call double,$(2))),$(1))
# return 0 1 0 1 ..., as many words as $(1)
alternating_bits=$(wordlist 1,$(words $(1)),$(patsubst %,0 1,$(1)))
# Produce as many unique words as there are words in $(1)
unique=$(call binseq,,$(call alternating_bits,$(1)))
# Sort $(1) without eliminating duplicates. $(1) may not contain /.
sorted_keep_dups=$(subst /,,$(dir $(sort $(join $(1:=/),$(call unique,$(1))))))
dups_from_sorted2=$(filter $(patsubst %0,%,$(filter %0,$(1))),$(patsubst %1,%,$(filter %1,%,$(1))))
# Given a sorted list, return all duplicates.
dups_from_sorted=$(sort $(call dups_from_sorted2,$(join $(1),$(call alternating_bits,$(1)))))
dups=$(call dups_from_sorted,$(call sorted_keep_dups,$(1)))
(2) 对单词列表的不同分区重复使用$(filter),使得每对单词在$(filter)的不同args中至少出现一次:
# given 0 1 0 1 0 1 0 1 ... , return 0 0 1 1 0 0 1 1 ...
double=$(wordlist 1,$(words $(1)),$(subst 0,0 0,$(subst 1,1 1,$(1))))
# given words with suffix 0 or 1, remove suffixes and return the words
# that occur both with 0 and 1 as suffix
filter_dups=$(filter $(patsubst %0,%,$(filter %0,$(1))),$(patsubst %1,%,$(filter %1,$(1))))
_dups=$(if $(findstring 1,$(2)),$(call filter_dups,$(join $(1),$(2)))
$(call _dups,$(1),$(call double,$(2))))
# return 0 1 0 1 ..., as many words as $(1)
alternating_bits=$(wordlist 1,$(words $(1)),$(patsubst %,0 1,$(1)))
# given a list of words, return the list of words that occur twice
dups=$(sort $(call _dups,$(1),$(call alternating_bits,$(1))))
这两种方法都有效且速度足够快,但它们很难阅读和理解。有没有更简单的方法可以接受(次二次)速度?
【问题讨论】:
-
我认为第二种方法是 O(N^2) 因为 filter_dups 中的过滤操作是 N/2 * N/2 (除非 make 会对我认为没有的参数进行排序) .我错了吗?
-
Make 创建第二个参数中单词的哈希表。所以过滤器是 O(N log N),其中 log N 部分增长非常缓慢。 (如果字数很少,那么 make 会做一个普通的二次搜索)