我在互联网上发现了以下问题,并想知道我将如何解决它:
您将获得一个包含0和1的数组。找到O(n)时间和O(1)空间算法,找到具有相同数量的1和0的最大子序列。
示例:
10101010
- 满足问题的最长子序列是输入本身- 醇>
1101000
- 满足该问题的最长子序列是110100
答案 0 :(得分:13)
<强>更新强>
我必须完全改写我的回答。 (如果你赞成早期版本,那么,你被欺骗了!)
让我们再次总结一下这个简单的案例,以便将其排除在外:
找到包含的位字符串的最长前缀 相同数量的1和0 阵列。
这是微不足道的:需要一个简单的计数器,计算我们有多少1比0,并在保持这个时迭代bitstring。此计数器最后一次变为零的位置是最长搜索前缀的结束。 O(N)时间,O(1)空间。 (我现在完全相信这是原始问题所要求的。)
现在让我们切换到更难的问题版本:我们不再需要子序列作为前缀 - 它们可以从任何地方开始。
经过一番来回思考,我认为可能没有线性算法。例如,考虑前缀“111111111111111111 ...”。这些中的每一个可能是最长子序列的开始,没有候选子序列起始位置支配(即总是提供比其他任何位置更好的解决方案),因此我们不能丢弃它们中的任何一个(O(N)在任何一步,我们必须能够在O(1)时间内从线性多个候选者中选择最佳开始(其具有与当前位置相同数量的1和0) 。 事实证明这是可行的,也很容易实现,因为我们可以根据1s(+1)和0s(-1)的运行总和选择候选者,这最多只有N,我们可以存储我们在2N单元格中达到每个总和的第一个位置 - 请参阅下面的pmod的答案(yellowfog的评论和几何见解)。
没有发现这个技巧,我用一个缓慢但确定的算法替换了一个快速但错误的(因为正确的算法比错误的算法更好!):
A
,其中从开始到该位置的累计数为1,例如如果bitstring是“001001001”,那么数组将是[0,0,1,1,1,2,2,2,3]。使用这个,我们可以在O(1)中测试子序列(i,j)是否有效:isValid(i, j) = (j - i + 1 == 2 * (A[j] - A[i - 1])
,即如果其长度是其中1s的两倍,则它是有效的。例如,子序列(3,6)是有效的,因为6 - 3 + 1 == 2 * A [6] - A [2] = 4. 普通的旧双循环:
maxSubsLength = 0 对于i = 1到N - 1 对于j = i + 1到N. if isValid(i,j)... #maintain maxSubsLength 结束 端
通过跳过比当前maxSubsLength短的i / j序列,使用一些分支绑定可以加快一点,但渐近地这仍然是O(n ^ 2)。慢,但有一个很大的优点:正确!
答案 1 :(得分:7)
严格地说,答案是没有这样的算法,因为由相同数量的零和1组成的字符串的语言不规则。
当然,每个人都忽略了这样一个事实:在空间中存储一个幅度n
的整数O(log n)
并在空间中将其视为O(1)
。 :-)几乎所有大的O,包括时间的,都充满(或者说是空的)缺失的log n
因子,或等效地,他们认为n
受机器字大小的限制,这意味着你真的在看一个有限的问题,一切都是O(1)
。
答案 2 :(得分:6)
新解决方案: 假设我们有n位输入位阵列2 * n大小的数组来保持位的位置。因此,数组元素的大小必须足够大,以保持最大位置数。对于256个输入位阵列,它需要256x2字节数组(字节足以保持255 - 最大位置)。
从位数组的第一个位置开始,我们使用规则将位置从数组的中间开始(索引为n):
<强> 1。如果我们传递“1”位则递增位置,当传递“0”位
时递减<强> 2。遇到已初始化的数组元素 - 不要更改它并记住位置之间的差异(当前减去数组元素) - 这是局部最大序列的大小。
第3。每当我们达到局部最大值时,将其与全局最大值进行比较,如果后者较小则更新。
例如:位序列 0,0,0,1,0,1
initial array index is n
set arr[n] = 0 (position)
bit 0 -> index--
set arr[n-1] = 1
bit 0 -> index--
set arr[n-2] = 2
bit 0 -> index--
set arr[n-3] = 3
bit 1 -> index++
arr[n-2] already contains 2 -> thus, local max seq is [3,2] becomes abs. maximum
will not overwrite arr[n-2]
bit 0 -> index--
arr[n-3] already contains 3 -> thus, local max seq is [4,3] is not abs. maximum
bit 1 -> index++
arr[n-2] already contains 2 -> thus, local max seq is [5,2] is abs. max
因此,我们只通过整个位数组一次。 这解决了这个任务吗?
input:
n - number of bits
a[n] - input bit-array
track_pos[2*n] = {0,};
ind = n;
/* start from position 1 since zero has
meaning track_pos[x] is not initialized */
for (i = 1; i < n+1; i++) {
if (track_pos[ind]) {
seq_size = i - track_pos[ind];
if (glob_seq_size < seq_size) {
/* store as interm. result */
glob_seq_size = seq_size;
glob_pos_from = track_pos[ind];
glob_pos_to = i;
}
} else {
track_pos[ind] = i;
}
if (a[i-1])
ind++;
else
ind--;
}
output:
glob_seq_size - length of maximum sequence
glob_pos_from - start position of max sequence
glob_pos_to - end position of max sequence
答案 3 :(得分:1)
在这个帖子(http://discuss.techinterview.org/default.asp?interview.11.792102.31)中,海报A.F.给出了一个在O(n)时间运行并使用O(sqrt(n log n))位的算法。
答案 4 :(得分:0)
暴力:以数组的最大长度开始计算o和l。如果等于l,你就完成了。否则将搜索长度减少1,并对缩短长度的所有子序列(即最大长度减去减少的长度)执行算法,依此类推。当减法为0时停止。
答案 5 :(得分:0)
正如用户“R ..”所指出的那样,严格来说,除非你忽略“log n”空间复杂性,否则没有解决方案。在下文中,我将考虑数组长度适合机器寄存器(例如64位字),并且机器寄存器的大小为O(1)。
需要注意的重要一点是,如果1比0更多,那么你要寻找的最大子序列必然包括所有的0,以及那么多的1。所以算法在这里:
符号:数组的长度为 n ,索引从0到 n-1 计算。
请注意,可能有多种解决方案(最大长度的几个子序列符合标准)。
简单来说:假设有1比0更多,那么我会考虑包含所有0的最小子序列。根据定义,该子序列被一束1包围。所以我从侧面抓住了足够的1个。
编辑:正如所指出的,这不起作用......“重点”实际上是错误的。
答案 6 :(得分:-1)
尝试这样的事情:
/* bit(n) is a macro that returns the nth bit, 0 or 1. len is number of bits */
int c[2] = {0,0};
int d, i, a, b, p;
for(i=0; i<len; i++) c[bit(i)]++;
d = c[1] < c[0];
if (c[d] == 0) return; /* all bits identical; fail */
for(i=0; bit(i)!=d; i++);
a = b = i;
for(p=0; i<len; i++) {
p += 2*bit(i)-1;
if (!p) b = i;
}
if (a == b) { /* account for case where we need bits before the first d */
b = len - 1;
a -= abs(p);
}
printf("maximal subsequence consists of bits %d through %d\n", a, b);
完全未经测试但模数愚蠢的错误应该有效。基于我对托马斯答案的答复,在某些情况下答案失败了。
答案 7 :(得分:-1)
新解决方案: O(1)的空间复杂度和时间复杂度O(n ^ 2)
int iStart = 0, iEnd = 0;
int[] arrInput = { 1, 0, 1, 1, 1,0,0,1,0,1,0,0 };
for (int i = 0; i < arrInput.Length; i++)
{
int iCurrEndIndex = i;
int iSum = 0;
for (int j = i; j < arrInput.Length; j++)
{
iSum = (arrInput[j] == 1) ? iSum+1 : iSum-1;
if (iSum == 0)
{
iCurrEndIndex = j;
}
}
if ((iEnd - iStart) < (iCurrEndIndex - i))
{
iEnd = iCurrEndIndex;
iStart = i;
}
}
答案 8 :(得分:-1)
我不确定你引用的数组是0和1的int数组还是bitarray?
如果是关于bitarray,这是我的方法:
int isEvenBitCount(int n)
{
//n ... //Decimal equivalent of the input binary sequence
int cnt1 = 0, cnt0 = 0;
while(n){
if(n&0x01) { printf("1 "); cnt1++;}
else { printf("0 "); cnt0++; }
n = n>>1;
}
printf("\n");
return cnt0 == cnt1;
}
int main()
{
int i = 40, j = 25, k = 35;
isEvenBitCount(i)?printf("-->Yes\n"):printf("-->No\n");
isEvenBitCount(j)?printf("-->Yes\n"):printf("-->No\n");
isEvenBitCount(k)?printf("-->Yes\n"):printf("-->No\n");
}
使用按位运算,时间复杂度也几乎为O(1)。