查找字符串是否仅包含给定字符集的最佳方法/算法

uye*_*tch 12 c++ string algorithm

我在接受采访时被问到这个问题,如果你要找出一个字符串是否只包含一组给定的字符.例如,让字符串集成为{0,1,2,3,4,5,6,7,8,9}上的所有字符串,即所有"数字"字符串.其中,如果{3,8,5}上的字符串集只是有效字符串,如何检查该字符串是否仅包含有效字符.说:

Input 8888338385
     Output VALID
Input 887837348234 
Output : Invalid
Run Code Online (Sandbox Code Playgroud)

我建议的方式是暴力,需要检查给定字符串中的每个字符与无效字符列表.如果任何一个字符无效,我将跳过检查所有其他字符并显示失败消息.但是,如此处所示,可能有更好的算法.请帮忙.

Nic*_*nes 12

编辑:感谢Luc Touraille对原始算法的巨大改进.

创建一个a[10]布尔数组.对于每个预期的数字e,设置a[e] = true.

现在,对于d输入中的每个数字,检查是否a[d]为真.如果不是,则返回false.如果他们都成功了,那就回归真实.

您可以将此概括为具有256个元素数组的所有ASCII字符.

如果您的输入字符串是长度N,您的比较字符串是长度M,字母表中的字母数是A,那么复杂度是O(N + M)(扫描您的两个字符串)加上O(A)(到初始化布尔数组).因此,除非您的字符串长度接近或大于您的字母大小,否则这可能不是最佳的.

值得指出的是,就Niklas Baumstark的出色性能比较而言,我们的两种解决方案实际上是相同的.这里构造的布尔数组您在双状态DFA中构建的转换表相同 [c 1 c 2 ...]*.我想,唯一的区别是Java的实现更加通用,会带来更多的开销.

  • 从预期的设定开始不是更快吗?这样,您只需要一个数组,并且只要找到无效字符就可以停止对输入字符串的迭代.你最多只能进行M + N次操作. (3认同)
  • 我实现了[基准来将其与基于正则表达式的解决方案进行比较](http://ideone.com/oPKYq).令我惊讶的是,Java的正则表达式实现似乎非常糟糕,它比这个解决方案慢了一个数量级!我原本期望正则表达式引擎也为这么大的字符类创建一个查找表,但显然它没有. (2认同)

Nik*_* B. 6

免责声明:反对我的假设,爪哇好像在优化这里使用的正则表达式,从而导致unperformant代码.即使Javascript的正则表达式似乎比这更快.基准测试还显示尼克的解决方案非常快.

这绝对是正则表达式的任务.在Java中:

public boolean isValidString(String str) {
  return str.matches("[358]*");
}
Run Code Online (Sandbox Code Playgroud)

这应该是O(n)最糟糕的情况,并且不能比这更好,因为每个角色都必须被看待.

如果性能至关重要,您可能希望缓存预编译的模式匹配器:

import java.util.regex.Pattern;

public class Matcher {
  private Pattern pattern;

  public Matcher() {
    this.pattern = Pattern.compile("[358]*");
  }

  public isValid(String str) {
    return pattern.matcher(str).matches();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @NiklasBaumstark - 这是一个面试问题.如果他们对解决方案的复杂性感兴趣,那么解决方案将是"O(M*N)",其中M是允许的字符集的大小,N是字符串的长度.如果问题只是在现实世界的场景中快速有效地编码,当然你的解决方案很棒. (2认同)
  • @WeaselFox:看起来你是对的.Java的正则表达式实现似乎[非常糟糕](http://ideone.com/oPKYq).我发现这既令人惊讶又有点搞笑,对于像这样的有限状态机来说,优化真的不那么难.即便是Javascript的正则表达式在这个例子中表现得更好...... (2认同)