【问题标题】:Longest substring where every character appear even number of times (possibly zero)每个字符出现偶数次(可能为零)的最长子字符串
【发布时间】:2018-11-06 22:44:15
【问题描述】:

假设我们有一个字符串s。我们希望找到s 的最长子字符串的长度,使得子字符串中的每个字符出现偶数次(可能为零)。
厕所时间:O(nlgn)。卫生间空间:O(n)

首先,很明显子字符串的长度必须是偶数。其次,我熟悉滑动窗口方法,我们在其中锚定一些 right 索引并寻找最左边的索引以匹配您的标准。我试图在这里应用这个想法,但无法真正制定它。

另外,在我看来,优先级队列可能会派上用场(因为 O(nlgn) 要求有点暗示)

我很乐意提供帮助!

【问题讨论】:

  • 你说的“子串”是指连续的子串,还是可以省略字符(即子串)?
  • 是的,连续子串(不是子序列)
  • 这么想,其他的就很琐碎了。还是想澄清一下。
  • 字母大小是否被认为是恒定的?
  • @amit,是的,为简单起见,我们甚至可以假设只有a-z 字符可能会出现(实际上它也只是我的想法,可能会有所帮助)

标签: algorithm computer-science


【解决方案1】:

让我们定义以下位集:

B[c,i] = 1 if character c appeared in s[0,...,i] even number of times.

计算B[c,i] 需要线性时间(对于所有值):

for all c:
  B[c,-1] = 0
for i from 0 to len(arr):
  B[c, i] = B[s[i], i-1] XOR 1

由于字母表的大小是恒定的,因此位集也是(对于每个i)。

注意条件:

子字符串中的每个字符出现偶数次

对于子字符串s[i,j] 为真当且仅当索引i 的位集与索引j 的位集相同(否则,在此子字符串中有一个位重复奇数次;其他方向: 如果有一个位重复多次,那么它的位就不可能相同。

因此,如果我们将所有位集存储在某个集合(哈希集/树集)中,并且只保留最新条目,则此预处理需要 O(n)O(nlogn) 时间(取决于哈希/树集)。

在第二次迭代中,对于每个索引,找到具有相同位集的更远索引(O(1)/O(logn),取决于哈希/树集),找到子字符串长度,并将其标记为候选。最后,选择最长的候选人。

这个解决方案是O(n) 空间用于位集,O(n)/O(nlogn) 时间,取决于是否使用哈希/树解决方案。


伪代码:

def NextBitset(B, c): # O(1) time
  for each x in alphabet \ {c}:
    B[x, i] = B[x, i-1]
   B[c, i] = B[c, i-1] XOR 1

for each c in alphabet: # O(1) time
  B[c,-1] = 0
map = new hash/tree map (bitset->int)

# first pass: # O(n)/O(nlogn) time
for i from 0 to len(s):
   # Note, we override with the latest element.
   B = NextBitset(B, s[i])
   map[B] = i

for each c in alphabet: # O(1) time
  B[c,-1] = 0
max_distance = 0
# second pass: O(n)/ O(nlogn) time.
for i from 0 to len(s):
  B = NextBitset(B, s[i])
  j = map.find(B)  # O(1) / O(logn)
  max_distance = max(max_distance, j-i)

【讨论】:

  • 谢谢阿米特!所以对于“第二次迭代”,我会做某种二进制搜索? (否则需要O(n^2)
  • 哦,没关系,你提到了一个比较需要O(1)/O(lgn),因为它是一个位集。
  • @Elimination 重要的是它是一个 constant size 的 bitset :)
  • 等一下,有些不对劲:如果您为每个索引执行第二阶段,您最终会得到O(n^2)。你是如何得到线性时间的? (假设比较两个位集的时间恒定)
  • @Elimination 在第二阶段,你只需对每个索引进行一次映射查找,即 O(1)/O(logn),我将添加一个伪代码来澄清。
【解决方案2】:

我不确定 amit 的具体建议是什么,如果是这样,请考虑另一种解释。这可以在一次遍历中完成。

为字符串的每个索引生成一个长度等于字母表的位集。存储遍历字符串时遇到的每个唯一位集的第一个索引。更新当前和之前看到的位集之间的最大间隔。

例如,字符串“aabccab”:

  a a b c c a b
  0 1 2 3 4 5 6 (index)

                _
0 1 0 0 0 0 1 1  |  (vertical)
0 0 0 1 1 1 1 0  |  bitset for
0 0 0 0 1 0 0 0 _|  each index
  ^           ^
  |___________|

   largest interval
   between current and
   previously seen bitset

每次迭代的更新可以在 O(1) 内完成,方法是使用前一个位集将每个字符的位掩码预处理为 XOR

bitset     mask
  0         1     1
  1   XOR   0  =  1
  0         0     0

表示更新与字母位集中第一位相关的字符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-12
    • 1970-01-01
    • 2010-10-20
    • 2022-10-15
    • 2014-08-31
    • 2011-10-02
    相关资源
    最近更新 更多