ikegami的解决方案将占用指数空间来存储在将字符串转换为正则表达式之前的字符串(每个单词将出现2
n-1 次,其中n是单词数,因此总空间至少为2
n-1 * Sum(单词长度))。这与正则表达式引擎无关 - 因为问题出现在字符串转换为正则表达式之前。
一个等价于ikegami解决方案的正则表达式构造(在匹配的字符串集方面)是:
^(?=[^ ])(?:(?: |^)John(?= |\z))?+(?:(?: |^)Von(?= |\z))?+(?:(?: |^)Neumann(?= |\z))?+\z
这只占用线性空间,即单词的数量和所有单词的总长度。
为了清晰明了:
^
(?=[^ ])
(?:(?: |^)John(?= |\z))?+
(?:(?: |^)Von(?= |\z))?+
(?:(?: |^)Neumann(?= |\z))?+
\z
正向先行断言 (?=[^ ])
有两个目的:避免匹配空字符串和确保第一个字符不是空格。
注意 ?+
,它使量词成为占有量词(或原子量词),因为我们在这里不需要回溯。 为什么? 如果我们按照正常方式操作,我们将循环遍历单词列表并将其与输入中最左侧的单词进行比较。找到匹配项后,我们将继续循环以将其与输入中的下一个单词进行比较,直到找到所有输入的单词或者我们已经完成了所有单词列表的循环。
占有量词还可以防止出现回溯错误。如果一个单词被认为是匹配的,那么它将永远不会再次被重新考虑。
对于每个单词,它们可以由空格前导,或者是字符串的开头。正向先行断言 (?= |\z)
的目的是确保具有相同前缀的单词不会在第一次尝试匹配时被错误匹配(例如 "John Von Vone"
,尝试匹配 "John Vone"
)。
由于没有回溯,最坏情况的性能与不使用正则表达式时的所有单词和输入字符串的长度成线性关系。
我们可以稍微改变正则表达式以允许灵活的间距:
^(?= *+[^ ])(?: *+John(?= |\z))?+(?: *+Von(?= |\z))?+(?: *+Neumann(?= |\z))?+ *+\z
为了更好的阐述(前面的空格很重要):
^
(?= *+[^ ])
(?: *+John(?= |\z))?+
(?: *+Von(?= |\z))?+
(?: *+Neumann(?= |\z))?+
*+
\z
在正则表达式的开头,使用前瞻 (?= *+[^ ])
确保输入字符串不仅仅包含空格。
为允许单词之前有任意数量的空格(使用占有量词不回溯),改变了正则表达式。对于单词恰好位于字符串开头的情况,使用0个或更多个量词*
。由于前瞻断言 (?= |\z)
的存在,没有2个单词会发生碰撞。
在构建字符串时(输入到正则表达式引擎之前),它仍然占据线性空间。最坏情况下的性能也是线性的。
极端情况
The original words:
aaaaaaaaaaaaaaaaaaa0 aaaaaaaaaaaaaaaaaaa1 ... aaaaaaaaaaaaaaaaaaa9 aaaaaaaaaaaaaaaaaaaa ... aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaaA ... aaaaaaaaaaaaaaaaaaaZ
(Each word is 20 characters, the last character changes from 0-9
, then a-z
, then A-Z
)
Strings to search for (not matching):
aaaaaaaaaaaaaaaaaaaz aaaaaaaaaaaaaaaaaaay
(y
can only come before z
)
The original word:
patterns used in Perl pattern matching evolved from those supplied
(Some normal words)
Strings to search for (not matching):
patterns used in Perl pattern matching evolved from those suppliedd
(Extra d
at the end)
The original word:
aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaa
(Word contains only a
, with different length.)
Strings to search for (not matching):
aaaaaaaaaaaa aaaaaaaaaaa aaaaaaaaaa aaaaaaaaa aaaaaaaa aaaaaaa aaaaaa aaaaa aaaaa
(Extra a
at the end)
The original word:
performance abc xyz performance 456 !@
(Same word appearing multiple times)
Strings to search for (not matching):
performance performance performance performance
/^(?=.)(?:\bjohn\b)?\s*(?:\bvon\b)?\s*(?:\bneumann\b)?\z/i
(在一些帮助下)。我不确定为什么他删除了它。这不是通用解决方案,但OP可能不需要通用解决方案。 - ikegami(?=.)
需要改为(?=\S)
,否则模式将匹配一串空格。 - Borodin