对长字符串中的有效单词进行标记

时间:2010-08-24 06:23:03

标签: algorithm string parsing

假设您有一个包含有效单词的字典。

给定一个删除了所有空格的输入字符串,确定该字符串是否由有效单词组成。

您可以假设字典是提供O(1)查找的哈希表。

Some examples:

helloworld-> hello world (valid)
isitniceinhere-> is it nice in here (valid)
zxyy-> invalid

如果一个字符串有多个可能的解析,只需返回true即可。

字符串可能很长。因此,想一个兼顾空间和算法的算法。时间有效。

2 个答案:

答案 0 :(得分:6)

我认为所有字符串的集合作为有效单词的连接(从有限字典中取出的单词)形成了字符字母表上的常规语言。然后,您可以构建一个完全接受所需字符串的有限自动机;计算时间是O(n)。

例如,让字典由单词{bat,bag}组成。然后我们构造以下自动机:状态用0,1,2表示。边缘:(0,1,b),(1,2,a),(2,0,t),(2,0,g) ;其中三元组(x,y,z)表示在输入z上从x到y的边缘。唯一接受状态为0.在每个步骤中,在读取下一个输入符号时,您必须计算该输入可达的状态集。鉴于自动机中的状态数是恒定的,这是复杂度O(n)。至于空间复杂性,我认为你可以用O(字数)和上面的构造提示。

对于另一个例子,使用单词{bag,bat,bun,but},自动机将如下所示:alt text

假设已经构建了自动机(执行此操作的时间与单词的长度和数量有关:-)我们现在认为决定自动机是否接受字符串的时间是O( n)其中n是输入字符串的长度。 更正式地说,我们的算法如下:

  1. 设S为一组状态,最初包含起始状态。
  2. 阅读下一个输入字符,让我们用a。
  3. 表示
  4. 对于S中的每个元素,确定我们在读取时从s进入的状态;也就是说,状态r使得上面的符号(s,r,a)是边缘。让我们用R表示这些状态的集合。即,R = {r | S中的s,(s,r,a)是边缘}。
  5. (如果R为空,则不接受该字符串并停止算法。)
  6. 如果没有其他输入符号,请检查是否有任何接受状态在R中。(在我们的例子中,只有一个接受状态,即起始状态。)如果是,则接受该字符串,否则,不接受字符串。
  7. 否则,请按S:= R并转到2.
  8. 现在,这个循环的执行次数和输入符号一样多。我们唯一要检查的是步骤3和5需要不断的时间。假设S和R的大小不大于自动机中的状态数,这是常数,并且我们可以以查找时间恒定的方式存储边缘,接下来是这样。 (请注意,我们当然会丢失多个'解析',但这也不是必需的。) 我认为这实际上被称为常规语言的成员问题,但我找不到合适的在线参考。

答案 1 :(得分:0)

我会选择带隐式回溯的递归算法。功能签名:f: input -> resultinput为字符串,resulttruefalse,具体取决于整个字符串是否可以正确标记。

像这样工作:

  1. 如果input为空字符串,请返回true
  2. 查看input的长度为一的前缀(即第一个字符)。如果它在字典中,请在后缀f上运行input。如果返回true,也请返回true
  3. 如果上一步中的长度为一的前缀不在字典中,或者上一步中f的调用返回false,则将前缀加长一,并在步骤2重复。如果不能再生成前缀(已经在字符串的末尾),请返回false
  4. 冲洗并重复。
  5. 对于具有低到中等量的模糊前缀的词典,这个应该在实践中获得相当好的运行时间(在一般情况下为O(n),我会说),尽管在理论上,可能构建具有O(2 ^ n)复杂度的病理病例。然而,我怀疑我们可以做得更好,因为我们无论如何都需要回溯,所以使用传统预先计算的词法分析器的“本能”O(n)方法是不可能的。 ......我想。

    编辑:平均案例复杂度的估算可能不正确,请参阅我的评论。

    空间复杂性只是堆栈空间,所以即使在最坏的情况下也是O(n)。