Algorithm 每个字符出现偶数次(可能为零)的最长子字符串

Algorithm 每个字符出现偶数次(可能为零)的最长子字符串,algorithm,computer-science,Algorithm,Computer Science,假设我们有一个字符串s。我们希望找到s中最长子字符串的长度,以便子字符串中的每个字符出现偶数次(可能为零)。 WC时间:O(nlgn)。厕所空间:O(n) 首先,很明显,子字符串的长度必须是偶数。其次,我熟悉滑动窗口方法,其中我们锚定一些右索引,并寻找最左边的索引以符合您的标准。我试着在这里应用这个想法,但无法真正表达出来 此外,在我看来,优先级队列可能会派上用场(因为O(nlgn)要求在某种程度上暗示了这一点) 我很高兴能得到帮助 让我们定义以下位集: B[c,i] = 1 if charac

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

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

此外,在我看来,优先级队列可能会派上用场(因为
O(nlgn)
要求在某种程度上暗示了这一点)


我很高兴能得到帮助

让我们定义以下位集:

B[c,i] = 1 if character c appeared in s[0,...,i] even number of times.
计算
B[c,i]
需要线性时间(对于所有值):

由于字母表的大小是恒定的,所以位集也是恒定的(对于每个
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)

我不确定阿米特的提议是什么,如果是这样的话,请考虑另一个解释。这可以在一次遍历中完成

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

例如,字符串“aabccab”:

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

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

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

当您说“子字符串”时,您是指连续的子字符串,还是可以省略字符(即子序列)?是的,连续的子字符串(不是子序列)是这样认为的,另一个将非常简单。还是想澄清一下。字母表的大小是恒定的吗?@amit,是的,为了简单起见,我们甚至可以假设只会出现
a-z
字符(实际上我也想到了,可能会有帮助)谢谢你,amit!所以对于“第二次迭代”,我会做一些二进制搜索?(否则需要
O(n^2)
)哦,没关系,你提到过一次比较需要
O(1)/O(lgn)
,因为它是一个位集。@消除重要的是它是一个大小恒定的位集:)等一下,有点不对:如果你对每个索引都进行第二阶段的比较,你最终会得到
O(n^2)
。你是怎么得到线性时间的?(假设比较两个位集的时间为常数)@Elimination在第二阶段,对于每个索引,您只需执行映射查找,即O(1)/O(logn),我将添加一个伪代码来澄清。
  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
bitset     mask
  0         1     1
  1   XOR   0  =  1
  0         0     0