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

时间:2012-02-02 13:31:20

标签: c++ string algorithm

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

Input 8888338385
     Output VALID
Input 887837348234 
Output : Invalid

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

5 个答案:

答案 0 :(得分:12)

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

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

现在输入中的每个数字d,检查a[d]是否为真。如果不是,则返回false。如果它们都成功,则返回true。

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

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

值得指出的是,就Niklas Baumstark的优秀performance comparison而言,我们的两个解决方案实际上是相同的。这里构造的布尔数组与你在双态DFA中构建的转换表相同 sub <1> c 2 ...] *。我认为唯一的区别是Java的实现更加通用,会带来更多的开销。

答案 1 :(得分:6)

免责声明:根据我的假设,Java似乎suck优化了此处使用的正则表达式,从而导致代码无法使用。即使Javascript的正则表达式似乎比这更快。基准测试还显示尼克的解决方案非常快。

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

public boolean isValidString(String str) {
  return str.matches("[358]*");
}

这应该是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();
  }
}

答案 2 :(得分:3)

您可以为允许的集合中的每个字符使用地图(如果字母表的范围有限),并直接检查字符串中的每个字符,检查它是否在地图中。这种方式只有O(N),其中N是字符串长度,而不是O(N * M),其中M是允许字符集。如果字母表规模大于另一个数据结构可用于存储允许的字符 - 例如排序树,例如O(N)logN的复杂性。

答案 3 :(得分:3)

对于c或c ++,你可以这样做:

const char* haystack = "8888338385";
const char* filter = "385";

if (strlen(haystack) != strspn(haystack, filter))
{
  // oops - haystack contains more characters...
}

c ++(std::string

存在等效的std::string::find_first_not_of函数 编辑:我知道这是作弊,但问题中没有任何内容可以排除这种情况。

答案 4 :(得分:-2)

我首先会对无效字母的输入和列表进行排序,然后您可以随时确定字符串是否在线性时间内有效