我正在尝试验证密码,无论它们在字符串中的位置如何,都不允许出现 3 个重复字符。
例如 :
121121 - 不接受,因为 1 出现超过 3 次。
121212 - 接受,因为 1 和 2 只出现了 3 次
我试过这个
([0-9])\1{2,}
Run Code Online (Sandbox Code Playgroud)
但它只验证连续重复的数字。
我不建议对这样的事情使用正则表达式,因为将密码收集到Map维护每个字符计数的位置会更容易。然后,您可以检查是否存在计数大于 的任何字符3:
password.chars()
.boxed()
.collect(Collectors.groupingBy(i -> i, Collectors.counting()))
.values()
.stream()
.anyMatch(i -> i > 3);
Run Code Online (Sandbox Code Playgroud)
这将返回true如果存在某些字符password出现超过3次,false否则。
正则表达式解决方案的效率非常低。请考虑纯粹出于学术兴趣来对待这个答案。
出现 4 次或多次相同字符的字符串失败的模式是
^(?!.*(.).*\1.*\1.*\1).*
Run Code Online (Sandbox Code Playgroud)
.*如果您需要精确该模式,则可以将最后一个模式替换为更具限制性的模式。
请参阅正则表达式演示。
这里的主要部分是(?!.*(.).*\1.*\1.*\1)负前瞻。它匹配任何 0+ 字符(如果Pattern.DOTALL使用,则匹配任何字符,包括换行符),尽可能多,然后匹配并将任何字符捕获(使用(.))到组 1 中,然后匹配任何 0+ 字符后跟相同的字符 3 次。如果找到(匹配)模式,则整个字符串匹配失败。
为什么效率低下?该模式很大程度上依赖于回溯。.*抓取字符串末尾的所有字符,然后引擎回溯,尝试为后续子模式容纳一些文本。您可能会在此处看到回溯步骤。数量越多.*,该模式就越消耗资源。
为什么惰性变体没有更好?对于某些字符串,看起来^(?!.*?(.).*?\1.*?\1.*?\1).*速度更快,如果重复字符彼此靠近并且与字符串的开头靠近,速度也会更快。如果它们位于字符串的末尾,效率就会降低。因此,如果前一个正则表达式匹配12121277 个步骤,则当前正则表达式也将采用相同数量的步骤。然而,如果你对其进行测试1212124444,你会发现惰性变体将在 139 步后失败,而贪婪变体将在 58 步后失败。反之亦然,4444121212将导致惰性正则表达式失败得更快,14 个步骤vs. 211 个步骤(贪婪变体)。
在Java中,你可以使用它
s.matches("(?!.*(.).*\\1.*\\1.*\\1)")
Run Code Online (Sandbox Code Playgroud)
或者
s.matches("(?!.*?(.).*?\\1.*?\\1.*?\\1)")
Run Code Online (Sandbox Code Playgroud)
在生产中使用Jacob 的解决方案。