给定一个仅由小写字母组成的 haystack 字符串(单个单词)和一个仅包含唯一小写字母的字符掩码,如何确定字符掩码中的所有字母是否在 haystack 字符串中的任何点连续出现?字符掩码中的字母可以按任何顺序使用,并且如有必要,可以多次使用以形成限定字符串。
测试字符串并注释预期的布尔结果:
$tests = [
['word' => 'example', 'mask' => 'lmp'], // true (mpl)
['word' => 'goodness', 'mask' => 'dns'], // false (dn, ss)
['word' => 'slippers', 'mask' => 'eip'], // true (ippe)
['word' => 'slippers', 'mask' => 'ips'], // false (s, ipp, s)
['word' => 'google', 'mask' => 'go'], // true (goog)
['word' => 'food', 'mask' => 'go'], // false (oo)
['word' => 'bananas', 'mask' => 'ans'], // true (ananas)
['word' => 'candle', 'mask' => 'ace'], // false (ca, e)
['word' => 'mississippi', 'mask' => 'i'], // true (i)
['word' => 'executive', 'mask' => 'ecitx'], // false (exec, ti, e)
];
Stack Overflow 上有许多预先存在的问题,涉及一系列具有类似要求的语言,但它们没有相同的规则组合(或者不在 PHP 中)。在这种情况下,限定子字符串必须完全由掩码中的字符组成,并且掩码中的所有字符必须至少使用一次。
这个问题是在另一位用户的一个有趣但不完整的问题被 Roomba 关闭、放弃和删除之后的抢救操作。
我任意添加了细节来澄清任务,限制了范围,并填充了一组测试用例。
我的第一个创作使用
preg_match_all()
提取连续的限定字符,然后使用提取的字母从字符掩码中删除字符。
preg_match_all("/[$mask]+/", $word, $m)
&& array_filter($m[0], fn($chars) => !ltrim($mask, $chars))
然后我意识到
preg_match_all()
可能是可以提前消除的匹配子字符串,因为它们的长度不足以清除掩码字符。 我根据掩码的长度向正则表达式添加了一个最小量词。 与模式可读性下降相比,额外的函数调用可能值得也可能不值得。
preg_match_all("/[$mask]{" . strlen($mask) . ",}/", $word, $m)
&& array_filter($m[0], fn($chars) => !ltrim($mask, $chars))
最后,我想看看是否可以仅使用正则表达式来完成该任务,并避免进行不必要的剩余匹配。 使用通常用于验证密码强度的技术,我调用
preg_replace()
来生成一系列前瞻,并构建了一个模式,preg_match()
可以使用该模式来确保掩码中的每个字母都存在于隔离的子字符串中。
(bool) preg_match('/' . preg_replace('/[a-z]/', "(?=[$mask]*$0)", $mask) . "[$mask]+/", $word)
对于哪一个最具可读性/最难维护,人们的意见会有所不同,我没有执行任何基准测试来看看哪一个性能最好。 这是 PHP 演示。