O(nlogn)算法 - 在二进制字符串中找到三个均匀间隔的算法

时间:2009-10-13 14:15:32

标签: algorithm big-o

我昨天在算法测试中遇到了这个问题,我无法弄清楚答案。它让我绝对疯狂,因为它值得大约40分。我认为大部分课程没有正确解决,因为我在过去24小时内没有提出解决方案。

给定长度为n的任意二进制字符串,如果存在则在字符串中找到三个均匀间隔的字符串。编写一个在O(n * log(n))时间内解决此问题的算法。

所以像这样的字符串有三个“均匀间隔”的字符串:11100000,0100100100

编辑:这是一个随机数,所以它应该可以适用于任何数字。我给出的例子是为了说明“均匀间隔”的属性。所以1001011是一个有效的数字。 1,4和7是均匀间隔的。

31 个答案:

答案 0 :(得分:127)

答案 1 :(得分:35)

您的问题在this paper(1999)中被称为AVERAGE:

  

问题是3SUM-hard如果存在问题的子二次减少3SUM:给定n个整数的集合A,A中的元素a,b,c是否a + b + c = 0?目前尚不清楚AVERAGE是否为3SUM-hard。但是,从AVERAGE到3SUM有一个简单的线性时间缩减,我们省略了其描述。

Wikipedia

  

当整数在[-u ... u]范围内时,通过将S表示为位向量并使用FFT执行卷积,可以在时间O(n + u lg u)中求解3SUM。

这足以解决您的问题:)。

什么是非常重要的是O(n log n)在零和1的数量方面是复杂的,而不是1的数量(可以作为数组给出,如[1] ,5,9,15])。检查一个集合是否具有算术级数,1的项数是很难的,并且根据1999年的那篇论文,没有比O(n 2 )更快的算法,并且推测它是不存在。 每个不考虑这一点的人都试图解决一个未解决的问题。

其他有趣的信息,大多数是无关紧要的:

下限:

一个简单的下限是类似Cantor的集合(数字1..3 ^ n-1在它们的三元扩展中不包含1) - 它的密度是n ^(log_3 2)(大约0.631)。因此,任何检查集合是否不是太大,然后检查所有对都不足以得到O(n log n)。你必须更聪明地调查序列。引用更好的下界here - 它是n 1-c /(log(n))^(1/2)。这意味着康托尔集合最优。

上限 - 我的旧算法:

众所周知,对于大n,不包含算术级数的{1,2,...,n}的子集最多具有n /(log n)^(1/20)个元素。论文On triples in arithmetic progression证明了更多:该集合不能包含多于n * 2 28 *(log log n / log n) 1/2 元素。所以你可以检查是否达到了这个界限,如果没有,天真地检查对。这是O(n 2 * log log n / log n)算法,比O(n 2 )快。不幸的是“On triples ......”在Springer上 - 但第一页是可用的,Ben Green的论述可用here,第28页,定理24。

顺便说一下,这些论文是从1999年开始的 - 与我提到的第一篇论文相同,所以这可能就是为什么第一篇论文没有提到这个结果。

答案 2 :(得分:8)

这不是一个解决方案,而是与Olexiy was thinking

类似的思路

我正在玩创建具有最大数量的序列,并且它们都非常有趣,我得到了125位数字,这是通过尝试插入尽可能多的“1”位而找到的前3个数字:

  • 11011000011011000000000000001101100001101100000000000000000000000000000000000000000110110000110110000000000000011011000011011
  • 10110100010110100000000000010110100010110100000000000000000000000000000000000000000101101000101101000000000000101101000101101
  • 10011001010011001000000000010011001010011001000000000000000000000000000000000000010011001010011001000000000010011001010011001

请注意,它们都是分形(考虑到约束,这并不太令人惊讶)。可能有一些东西在向后思考,也许如果字符串不是具有特征的分形,那么它必须具有重复模式?

感谢beta以更好的术语描述这些数字。

<强>更新 唉,从一个足够大的初始字符串开始,看起来模式会崩溃,例如:10000000000001:

100000000000011
10000000000001101
100000000000011011
10000000000001101100001
100000000000011011000011
10000000000001101100001101
100000000000011011000011010000000001
100000000000011011000011010000000001001
1000000000000110110000110100000000010011
1000000000000110110000110100000000010011001
10000000000001101100001101000000000100110010000000001
10000000000001101100001101000000000100110010000000001000001
1000000000000110110000110100000000010011001000000000100000100000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101
100000000000011011000011010000000001001100100000000010000010000000000000110100001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001
100000000000011011000011010000000001001100100000000010000010000000000000110100001001000001000000110001000000001000000000000000000000000000000000000000010000001000000000000001100000000110010000000010010000000000001000000001000010000010010001001000001000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000011000001000000000000000000000100100000000000000000000000000000000000011001000000000000000000000010010000010000001
1000000000000110110000110100000000010011001000000000100000100000000000001101000010010000010000001100010000000010000000000000000000000000000000000000000100000010000000000000011000000001100100000000100100000000000010000000010000100000100100010010000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000110000010000000000000000000001001000000000000000000000000000000000000110010000000000000000000000100100000100000011
10000000000001101100001101000000000100110010000000001000001000000000000011010000100100000100000011000100000000100000000000000000000000000000000000000001000000100000000000000110000000011001000000001001000000000000100000000100001000001001000100100000100000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000100000000000000000000010010000000000000000000000000000000000001100100000000000000000000001001000001000000110000000000001

答案 3 :(得分:6)

我怀疑一个看起来像O(n ^ 2)的简单方法实际上会产生更好的东西,比如O(n ln(n))。需要最长时间测试的序列(对于任何给定的n)是不包含三元组的序列,并且对序列中可以包含的1的数量施加严格的限制。

我想出了一些挥手的争论,但我找不到一个整洁的证据。我将在黑暗中采取刺痛:答案是一个非常聪明的想法,教授已经知道这么久以至于看起来很明显,但这对学生来说太难了。 (无论是那个,还是你在讲述它的讲座中都会睡觉。)

答案 4 :(得分:3)

修订:2009-10-17 23:00

我在大数字上运行它(比如,2000万字符串),我现在相信这个算法不是O(n logn)。尽管如此,它还是一个非常酷的实现,并且包含许多优化,使其运行得非常快。它在25秒内评估二进制字符串24或更少数字的所有排列。

我已经更新了代码,以包含今天早些时候的0 <= L < M < U <= X-1观察结果。


原始

这在概念上类似于another question I answered。该代码还查看了一系列中的三个值,并确定三元组是否满足条件。这是C#代码改编自:

using System;
using System.Collections.Generic;

namespace StackOverflow1560523
{
    class Program
    {
        public struct Pair<T>
        {
            public T Low, High;
        }
        static bool FindCandidate(int candidate, 
            List<int> arr, 
            List<int> pool, 
            Pair<int> pair, 
            ref int iterations)
        {
            int lower = pair.Low, upper = pair.High;
            while ((lower >= 0) && (upper < pool.Count))
            {
                int lowRange = candidate - arr[pool[lower]];
                int highRange = arr[pool[upper]] - candidate;
                iterations++;
                if (lowRange < highRange)
                    lower -= 1;
                else if (lowRange > highRange)
                    upper += 1;
                else
                    return true;
            }
            return false;
        }
        static List<int> BuildOnesArray(string s)
        {
            List<int> arr = new List<int>();
            for (int i = 0; i < s.Length; i++)
                if (s[i] == '1')
                    arr.Add(i);
            return arr;
        }
        static void BuildIndexes(List<int> arr, 
            ref List<int> even, ref List<int> odd, 
            ref List<Pair<int>> evenIndex, ref List<Pair<int>> oddIndex)
        {
            for (int i = 0; i < arr.Count; i++)
            {
                bool isEven = (arr[i] & 1) == 0;
                if (isEven)
                {
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count+1});
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count});
                    even.Add(i);
                }
                else
                {
                    oddIndex.Add(new Pair<int> {Low=odd.Count-1, High=odd.Count+1});
                    evenIndex.Add(new Pair<int> {Low=even.Count-1, High=even.Count});
                    odd.Add(i);
                }
            }
        }

        static int FindSpacedOnes(string s)
        {
            // List of indexes of 1s in the string
            List<int> arr = BuildOnesArray(s);
            //if (s.Length < 3)
            //    return 0;

            //  List of indexes to odd indexes in arr
            List<int> odd = new List<int>(), even = new List<int>();

            //  evenIndex has indexes into arr to bracket even numbers
            //  oddIndex has indexes into arr to bracket odd numbers
            List<Pair<int>> evenIndex = new List<Pair<int>>(), 
                oddIndex = new List<Pair<int>>(); 
            BuildIndexes(arr, 
                ref even, ref odd, 
                ref evenIndex, ref oddIndex);

            int iterations = 0;
            for (int i = 1; i < arr.Count-1; i++)
            {
                int target = arr[i];
                bool found = FindCandidate(target, arr, odd, oddIndex[i], ref iterations) || 
                    FindCandidate(target, arr, even, evenIndex[i], ref iterations);
                if (found)
                    return iterations;
            }
            return iterations;
        }
        static IEnumerable<string> PowerSet(int n)
        {
            for (long i = (1L << (n-1)); i < (1L << n); i++)
            {
                yield return Convert.ToString(i, 2).PadLeft(n, '0');
            }
        }
        static void Main(string[] args)
        {
            for (int i = 5; i < 64; i++)
            {
                int c = 0;
                string hardest_string = "";
                foreach (string s in PowerSet(i))
                {
                    int cost = find_spaced_ones(s);
                    if (cost > c)
                    {
                        hardest_string = s;
                        c = cost;
                        Console.Write("{0} {1} {2}\r", i, c, hardest_string);
                    }
                }
                Console.WriteLine("{0} {1} {2}", i, c, hardest_string);
            }
        }
    }
}

主要区别是:

  1. 彻底搜索解决方案
    此代码生成一组数据,以找到最难解决的算法。
  2. 所有解决方案与最难解决的问题 上一个问题的代码使用python生成器生成了所有解决方案。此代码只显示每个模式长度最难的。
  3. 评分算法
    此代码检查从中间元素到其左手边缘和右手边缘的距离。 python代码测试总和是高于还是低于0.
  4. 候选人的融合 当前代码从中间到边缘工作以找到候选者。上一个问题中的代码从边缘向中间工作。最后一次更改可以带来很大的性能提升。
  5. 使用偶数和奇数池 根据该记录结束时的观察结果,代码搜索偶数个奇数对的对,找到L和U,保持M固定。这通过预先计算信息减少了搜索次数。因此,代码在FindCandidate的主循环中使用两个间接级别,并且需要为每个中间元素调用两次FindCandidate:一次用于偶数,一次用于奇数。
  6. 一般的想法是处理索引,而不是数据的原始表示。计算出现1的数组允许算法在数据中与1的数量成比例地运行,而不是与数据的长度成比例的时间。这是一个标准的转换:创建一个数据结构,允许更快的操作,同时保持问题的等效。

    结果已过期:已删除。


    编辑:2009-10-16 18:48

    在yx的数据上,作为代表要计算的硬数据的代表,在其他响应中给出了一些信任,我得到了这些结果......我删除了这些。它们已经过时了。

    我想指出,这个数据对我的算法来说并不是最困难的,所以我认为yx的分形是最难解决的假设是错误的。我预计,特定算法的最坏情况将取决于算法本身,并且在不同算法中可能不一致。


    编辑:2009-10-17 13:30

    对此进一步观察。

    首先,将0和1的字符串转换为1的每个位置的索引数组。假设该数组A的长度为X.那么目标是找到

    0 <= L < M < U <= X-1
    

    这样

    A[M] - A[L] = A[U] - A[M]
    

    2*A[M] = A[L] + A[U]
    

    由于A [L]和A [U]总和为偶数,因此它们不能(偶数,奇数)或(奇数,偶数)。通过将A []拆分为奇数和偶数池并依次在奇数和偶数候选池中搜索A [M]上的匹配,可以改进对匹配的搜索。

    然而,我认为这更多是性能优化而不是算法改进。比较次数应该减少,但算法的顺序应该相同。


    编辑2009-10-18 00:45

    另一种优化发生在我身上,与将候选人分成偶数和奇数相同。由于三个索引必须加到3的倍数(a,a + x,a + 2x - mod 3为0,无论a和x),你可以将L,M和U分成它们的mod 3值:

    M  L  U
    0  0  0
       1  2
       2  1
    1  0  2
       1  1
       2  0
    2  0  1
       1  0
       2  2
    

    事实上,你可以将它与偶数/奇数观察结合起来,并将它们分成mod 6值:

    M  L  U
    0  0  0
       1  5
       2  4
       3  3
       4  2
       5  1
    

    等等。这将提供进一步的性能优化,但不是算法加速。

答案 5 :(得分:2)

尚未能提出解决方案:(,但有一些想法。

如果我们从一个反向问题开始怎么办:构造一个最大数为1且没有任何均匀间隔三重奏的序列。如果你可以证明1的最大数量是o(n),那么你可以通过仅迭代1的列表来改进你的估计。

答案 6 :(得分:2)

这可能会有所帮助......

此问题简化为以下内容:

  

给定一系列正整数,找到一个连续的子序列,分为前缀和后缀,使得子序列的前缀之和等于子序列后缀的总和。

例如,给定一系列[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ],我们会找到[ 3, 6, 5, 2, 2]的子序列,其前缀为[ 3, 6 ],前缀和为9,后缀为{ {1}}后缀总和为[ 5, 2, 2 ]

减少量如下:

  

给定一系列零和一,并从最左边开始,继续向右移动。每次遇到另一个时,记录自上次遇到的移动次数,并将该数字附加到结果序列中。

例如,给定一系列9,我们会发现[ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]的减少。通过此减少,我们计算[ 1, 3, 4]的连续子序列,[ 1, 3, 4]的前缀,总和为[ 1, 3],后缀为4,总和为[ 4 ]

此减少可以在4中计算。

不幸的是,我不确定从哪里开始。

答案 7 :(得分:1)

我想出了类似的东西:

def IsSymetric(number):
    number = number.strip('0')

    if len(number) < 3:
        return False
    if len(number) % 2 == 0:
        return IsSymetric(number[1:]) or IsSymetric(number[0:len(number)-2])
    else:
        if number[len(number)//2] == '1':
            return True
        return IsSymetric(number[:(len(number)//2)]) or IsSymetric(number[len(number)//2+1:])
    return False

这是受到andycjw的启发。

  1. 截断零。
  2. 如果再测试两个子串0 - (len-2)(跳过最后一个字符)和1 - (len-1)(跳过第一个字符)
  3. 如果不是因为中间的字符是一个而不是我们成功的话。否则将midle中的字符串除以midle元素并检查两个部分。
  4. 至于复杂度,这可能是O(nlogn),因为在每次递归中我们除以2。

    希望它有所帮助。

答案 8 :(得分:1)

好的,我将再次尝试解决这个问题。我想我可以通过使用平衡二叉树来存储1之间的距离来证明O(n log(n))算法与已经讨论过的算法类似。这种方法的灵感来自司法部关于将问题减少到1之间距离列表的观察。

我们是否可以扫描输入字符串以在1的位置周围构建平衡二叉树,使得每个节点存储1的位置,并且每个边缘用每个子节点到相邻1的距离标记。例如:

10010001 gives the following tree

      3
     / \
  2 /   \ 3
   /     \
  0       7

这可以在O(n log(n))中完成,因为对于大小为n的字符串,在最坏的情况下每个插入都需要O(log(n))。

然后问题是搜索树以发现在任何节点处是否存在从该节点通过左子节点的路径,该路径与通过右子节点的路径具有相同的距离。这可以在每个子树上递归完成。在搜索中合并两个子树时,我们必须比较左子树中路径的距离与右边路径的距离。由于子树中的路径数量与log(n)成正比,节点数量为n,我相信这可以在O(n log(n))时间内完成。

我错过了什么吗?

答案 9 :(得分:1)

我会在这里粗略猜测,并让那些计算复杂度更好的人帮助我了解我的算法如何以O符号方式表现

  1. 给定二进制字符串0000010101000100(例如)
  2. 庄稼的头部和尾部 - &gt; 00000 101010001 00
  3. 我们从以前的计算得到101010001
  4. 检查中间位是否为“1”,如果为真,则发现有效的三个均匀间隔的“1”(仅当位数为奇数时)
  5. 相关地,如果剩余的裁剪位数是偶数,则头部和尾部“一”不能是均匀间隔的“一”的一部分,
  6. 我们使用1010100001作为示例(额外的'0'成为偶数作物),在这种情况下我们需要再次裁剪,然后变成 - &gt; 10101 00001
  7. 我们从之前的计算得到10101,并检查中间位,我们再次找到均匀间隔的位
  8. 我不知道如何计算复杂性,有人可以帮忙吗?

    编辑:添加一些代码来说明我的想法

    edit2:试图编译我的代码并发现一些重大错误,修复了

    char *binaryStr = "0000010101000100";
    
    int main() {
       int head, tail, pos;
       head = 0;
       tail = strlen(binaryStr)-1;
       if( (pos = find3even(head, tail)) >=0 )
          printf("found it at position %d\n", pos);
       return 0;
    }
    
    int find3even(int head, int tail) {
       int pos = 0;
       if(head >= tail) return -1;
       while(binaryStr[head] == '0') 
          if(head<tail) head++;
       while(binaryStr[tail] == '0') 
          if(head<tail) tail--;
       if(head >= tail) return -1;
       if( (tail-head)%2 == 0 && //true if odd numbered
           (binaryStr[head + (tail-head)/2] == '1') ) { 
             return head;
       }else {
          if( (pos = find3even(head, tail-1)) >=0 )
             return pos;
          if( (pos = find3even(head+1, tail)) >=0 )
             return pos;
       }
       return -1;
    }
    

答案 10 :(得分:1)

一个有趣的问题,但是一旦你意识到两个'1'之间的实际模式无关紧要,算法就变成了:

  • 扫描寻找'1'
  • 从下一个位置扫描开始另一个'1'(到数组的末尾减去当前第一个'1'的距离,否则第三个'1'将超出界限)
  • 如果在第二个'1'的位置加上到第一个1'的距离,则找到第三个'1',我们将它们均匀分隔。

在代码中,JTest时尚,(注意这段代码写得不是最有效的,我添加了一些println来看看会发生什么。)

import java.util.Random;

import junit.framework.TestCase;

public class AlgorithmTest extends TestCase {

 /**
  * Constructor for GetNumberTest.
  *
  * @param name The test's name.
  */
 public AlgorithmTest(String name) {
  super(name);
 }

 /**
  * @see TestCase#setUp()
  */
 protected void setUp() throws Exception {
  super.setUp();
 }

 /**
  * @see TestCase#tearDown()
  */
 protected void tearDown() throws Exception {
  super.tearDown();
 }

 /**
  * Tests the algorithm.
  */
 public void testEvenlySpacedOnes() {

  assertFalse(isEvenlySpaced(1));
  assertFalse(isEvenlySpaced(0x058003));
  assertTrue(isEvenlySpaced(0x07001));
  assertTrue(isEvenlySpaced(0x01007));
  assertTrue(isEvenlySpaced(0x101010));

  // some fun tests
  Random random = new Random();

  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
  isEvenlySpaced(random.nextLong());
 }

 /**
  * @param testBits
  */
 private boolean isEvenlySpaced(long testBits) {
  String testString = Long.toBinaryString(testBits);
  char[] ones = testString.toCharArray();
  final char ONE = '1';

  for (int n = 0; n < ones.length - 1; n++) {

   if (ONE == ones[n]) {
    for (int m = n + 1; m < ones.length - m + n; m++) {

     if (ONE == ones[m] && ONE == ones[m + m - n]) {
      System.out.println(" IS evenly spaced: " + testBits + '=' + testString);
      System.out.println("               at: " + n + ", " + m + ", " + (m + m - n));
      return true;
     }
    }
   }
  }

  System.out.println("NOT evenly spaced: " + testBits + '=' + testString);
  return false;
 }
}

答案 11 :(得分:1)

我想到了一种可能有效的分而治之的方法。

首先,在预处理中,您需要将小于输入大小一半的所有数字( n / 3)插入列表中。

给定一个字符串:0000010101000100(请注意,此特定示例有效)

将所有素数(和1)从1到(16/2)插入一个列表:{1,2,3,4,5,6,7}

然后把它分成两半:

100000101 01000100

继续这样做,直到你得到大小为1的字符串。对于所有size-one字符串,其中包含1,将字符串的索引添加到可能性列表中;否则,返回-1表示失败。

您还需要返回与每个起始索引相关联的仍然可能的间距的列表。 (从你上面的列表开始,然后删除数字)这里,一个空列表意味着你只处理一个1,所以此时任何间距都是可能的;否则该清单包括必须排除的间距。

继续上面的例子:

1000 0101 0100 0100

10 00 01 01 01 00 01 00

1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0

在第一个组合步骤中,我们现在有八组两个。在第一个中,我们有一个集合的可能性,但我们知道间距为1是不可能的,因为其他零存在。所以我们返回0(对于索引)和{2,3,4,5,7},因为间距为1是不可能的。在第二个,我们什么都没有,所以返回-1。在第三个中,我们有一个匹配,在索引5中没有消除间距,所以返回5,{1,2,3,4,5,7}。在第四对中,我们返回7,{1,2,3,4,5,7}。在第五个,返回9,{1,2,3,4,5,7}。在第六,返回-1。在第七,返回13,{1,2,3,4,5,7}。在第八个,返回-1。

再次组合成四组四组,我们有:

1000:返回(0,{4,5,6,7}) 0101:返回(5,{2,3,4,5,6,7}),(7,{1,2,3,4,5,6,7}) 0100:返回(9,{3,4,5,6,7}) 0100:返回(13,{3,4,5,6,7})

组合成八组:

10000101:返回(0,{5,7}),(5,{2,3,4,5,6,7}),(7,{1,2,3,4, 5,6,7}) 01000100:返回(9,{4,7}),(13,{3,4,5,6,7})

结合成十六集:

10000101 01000100

随着我们的进步,我们一直在检查所有可能性。到目前为止,我们已经留下了超出字符串末尾的内容,但现在我们可以检查所有可能性。

基本上,我们检查前面的1和5和7的间距,并发现它们不排列为1。 (注意每个检查是CONSTANT,而不是线性时间)然后我们检查第二个(索引5)的间距为2,3,4,5,6和7--或者我们会,但是我们可以在2以后停止实际上匹配了。

唷!这是一个相当长的算法。

由于最后一步,我不知道100%是否是 O(n log n),但到那里的一切肯定是 O(n log n)据我所知。我稍后会再回过头来尝试完善最后一步。

编辑:改变了我的回答以反映Welbog的评论。对不起,错误。我稍后会写一些伪代码,当我有更多时间来破译我再写的内容时。 ; - )

答案 12 :(得分:1)

对于简单的问题类型(即你只搜索三个“1”只有(即零或更多)“0”之间),它非常简单:你可以在每个时分开序列“1”并寻找具有相同长度的两个相邻子序列(当然,第二子序列不是最后一个子序列)。显然,这可以在 O(n)时间内完成。

对于更复杂的版本(即搜索索引 i 和间隙 g > 0,使s[i]==s[i+g]==s[i+2*g]=="1"),我不确定,如果存在 O(n log n)解决方案,因为可能有 O(n²)三元组具有此属性(想到所有的字符串,有大约n²/ 2 这样的三胞胎)。当然,你只是在寻找其中一种,但我目前不知道,如何找到它......

答案 13 :(得分:0)

进入问题的一个方法是考虑因素和转变。

通过移位,您可以将1和0的字符串与其自身的移位版本进行比较。然后你选择匹配的。把这个例子换成两个:

1010101010
  1010101010
------------
001010101000

得到的1(按位AND)必须表示均匀间隔2的所有1。同样的例子换了三个:

1010101010
   1010101010
-------------
0000000000000

在这种情况下,没有1个均匀分开三个。

那么这告诉你什么?那么你只需要测试素数的变化。例如,假设您有两个1,它们相隔六个。你只需要测试'两个'班次和'三个'班次(因为这些班次除六)。例如:

10000010 
  10000010 (Shift by two)
    10000010
      10000010 (We have a match)

10000010
   10000010 (Shift by three)
      10000010 (We have a match)

所以你需要检查的唯一转变是2,3,5,7,11,13等。最接近最接近数字串大小的平方根的素数。

几乎解决了?

我认为我更接近解决方案。基本上是:

  1. 扫描字符串1。对于每个1个音符,它是取其位置模数后的余数。模数范围从字符串大小的1到一半。这是因为最大可能的分离大小是字符串的一半。这在O(n ^ 2)中完成。但。只需要检查素数模数,所以O(n ^ 2 / log(n))
  2. 首先按照最大模数对模数/余数列表进行排序,这可以在O(n * log(n))时间内完成。
  3. 寻找三个相同的模数/余数。
  4. 以某种方式找回那些人的位置!
  5. 我认为答案的最大线索是,最快的排序算法是O(n * log(n))。

    <强> WRONG

    如同事指出的那样,第1步是错误的。如果我们在位置2,12和102处有1个。然后取10的模数,它们将具有相同的余数,但是不是等间隔的!遗憾。

答案 14 :(得分:0)

# <algorithm>
def contains_evenly_spaced?(input)
  return false if input.size < 3
  one_indices = []
  input.each_with_index do |digit, index|
    next if digit == 0
    one_indices << index
  end
  return false if one_indices.size < 3
  previous_indexes = []
  one_indices.each do |index|
    if !previous_indexes.empty?
      previous_indexes.each do |previous_index|
        multiple = index - previous_index
        success_index = index + multiple
        return true if input[success_index] == 1
      end
    end
    previous_indexes << index
  end
  return false
end
# </algorithm>

def parse_input(input)
  input.chars.map { |c| c.to_i }
end

我遇到了数百万位数的最坏情况。来自/dev/urandom的模糊显然给你O(n),但我知道最糟糕的情况比这更糟糕。我只是不知道有多糟糕。对于小n,在3*n*log(n)附近找到输入是微不足道的,但是对于这个特定问题,很难将这些输入与其他增长顺序区分开来。

任何处理最坏情况输入的人都可以生成一个长度大于十万的字符串吗?

答案 15 :(得分:0)

这似乎是一个有趣的问题所以我决定尝试一下。

我假设111000001会找到前3个并获得成功。基本上1之后的零的数量是重要的,因为根据您的定义,0111000与111000相同。一旦你发现两个案例为1,那么下一个案例就完成了三部曲。

这是Python:

def find_three(bstring):
    print bstring
    dict = {}
    lastone = -1
    zerocount = 0
    for i in range(len(bstring)):
        if bstring[i] == '1':
            print i, ': 1'
            if lastone != -1:
                if(zerocount in dict):
                    dict[zerocount].append(lastone)
                    if len(dict[zerocount]) == 2:
                        dict[zerocount].append(i)
                        return True, dict
                else:
                    dict[zerocount] = [lastone]
            lastone = i
            zerocount = 0
        else:
            zerocount = zerocount + 1
    #this is really just book keeping, as we have failed at this point
    if lastone != -1:
        if(zerocount in dict):
            dict[zerocount].append(lastone)
        else:
            dict[zerocount] = [lastone]
    return False, dict

这是第一次尝试,所以我确信这可以用更干净的方式编写。请列出下面此方法失败的情况。

答案 16 :(得分:0)

这里没有理论上的答案,但是我写了一个快速的Java程序来探讨作为k和n函数的运行时行为,其中n是总位长,k是1的数。我和一些回答者说的是“常规”算法,它检查所有位对并查找第3位,即使在最坏的情况下需要O(k ^ 2),现实,因为最坏情况需要稀疏位串,是O(n ln n)。

无论如何,这是下面的程序。这是一个蒙特卡罗风格的程序,它运行大量试验NTRIALS用于常数n,并使用伯努利过程随机生成一系列k值的位集,其中密度约束在可指定的限制之间,并记录运行时间发现或未找到均匀间隔的三元组,时间以步长而非CPU时间测量。我运行n = 64,256,1024,4096,16384 *(仍在运行),首先进行500000次试验的试运行,看看哪个k值的运行时间最长,然后另一个试验用50000次试验缩小的试验 - 密度集中于看看这些值是什么样的。最长的运行时间确实发生在非常稀疏的密度下(例如,对于n = 4096,运行时间峰值在k = 16-64范围内,平均运行时间为4212步,温度峰值@ k = 31,最大运行时间峰值为5101步骤@ k = 58)。看起来在最坏情况下O(k ^ 2)步骤需要非常大的N值才能大于O(n)步骤,在此步骤中扫描bitstring以找到1的位置索引。

package com.example.math;

import java.io.PrintStream;
import java.util.BitSet;
import java.util.Random;

public class EvenlySpacedOnesTest {
    static public class StatisticalSummary
    {
        private int n=0;
        private double min=Double.POSITIVE_INFINITY;
        private double max=Double.NEGATIVE_INFINITY;
        private double mean=0;
        private double S=0;

        public StatisticalSummary() {}
        public void add(double x) {
            min = Math.min(min, x);
            max = Math.max(max, x);
            ++n;
            double newMean = mean + (x-mean)/n;
            S += (x-newMean)*(x-mean);
            // this algorithm for mean,std dev based on Knuth TAOCP vol 2
            mean = newMean;
        }
        public double getMax() { return (n>0)?max:Double.NaN; }
        public double getMin() { return (n>0)?min:Double.NaN; }
        public int getCount() { return n; }
        public double getMean() { return (n>0)?mean:Double.NaN; }
        public double getStdDev() { return (n>0)?Math.sqrt(S/n):Double.NaN; } 
        // some may quibble and use n-1 for sample std dev vs population std dev    
        public static void printOut(PrintStream ps, StatisticalSummary[] statistics) {
            for (int i = 0; i < statistics.length; ++i)
            {
                StatisticalSummary summary = statistics[i];
                ps.printf("%d\t%d\t%.0f\t%.0f\t%.5f\t%.5f\n",
                        i,
                        summary.getCount(),
                        summary.getMin(),
                        summary.getMax(),
                        summary.getMean(),
                        summary.getStdDev());
            }
        }
    }

    public interface RandomBernoulliProcess // see http://en.wikipedia.org/wiki/Bernoulli_process
    {
        public void setProbability(double d);
        public boolean getNextBoolean();
    }

    static public class Bernoulli implements RandomBernoulliProcess
    {
        final private Random r = new Random();
        private double p = 0.5;
        public boolean getNextBoolean() { return r.nextDouble() < p; }
        public void setProbability(double d) { p = d; }
    }   
    static public class TestResult {
        final public int k;
        final public int nsteps;
        public TestResult(int k, int nsteps) { this.k=k; this.nsteps=nsteps; } 
    }

    ////////////
    final private int n;
    final private int ntrials;
    final private double pmin;
    final private double pmax;
    final private Random random = new Random();
    final private Bernoulli bernoulli = new Bernoulli();
    final private BitSet bits;
    public EvenlySpacedOnesTest(int n, int ntrials, double pmin, double pmax) {
        this.n=n; this.ntrials=ntrials; this.pmin=pmin; this.pmax=pmax;
        this.bits = new BitSet(n);
    }

    /*
     * generate random bit string
     */
    private int generateBits()
    {
        int k = 0; // # of 1's
        for (int i = 0; i < n; ++i)
        {
            boolean b = bernoulli.getNextBoolean();
            this.bits.set(i, b);
            if (b) ++k;
        }
        return k;
    }

    private int findEvenlySpacedOnes(int k, int[] pos) 
    {
        int[] bitPosition = new int[k];
        for (int i = 0, j = 0; i < n; ++i)
        {
            if (this.bits.get(i))
            {
                bitPosition[j++] = i;
            }
        }
        int nsteps = n; // first, it takes N operations to find the bit positions.
        boolean found = false;
        if (k >= 3) // don't bother doing anything if there are less than 3 ones. :(
        {       
            int lastBitSetPosition = bitPosition[k-1];
            for (int j1 = 0; !found && j1 < k; ++j1)
            {
                pos[0] = bitPosition[j1];
                for (int j2 = j1+1; !found && j2 < k; ++j2)
                {
                    pos[1] = bitPosition[j2];

                    ++nsteps;
                    pos[2] = 2*pos[1]-pos[0];
                    // calculate 3rd bit index that might be set;
                    // the other two indices point to bits that are set
                    if (pos[2] > lastBitSetPosition)
                        break;
                    // loop inner loop until we go out of bounds

                    found = this.bits.get(pos[2]);
                    // we're done if we find a third 1!
                }
            }
        }
        if (!found)
            pos[0]=-1;
        return nsteps;
    }

    /*
     * run an algorithm that finds evenly spaced ones and returns # of steps.
     */
    public TestResult run()
    {
        bernoulli.setProbability(pmin + (pmax-pmin)*random.nextDouble());
        // probability of bernoulli process is randomly distributed between pmin and pmax

        // generate bit string.
        int k = generateBits();
        int[] pos = new int[3];
        int nsteps = findEvenlySpacedOnes(k, pos);
        return new TestResult(k, nsteps); 
    }

    public static void main(String[] args)
    {
        int n;
        int ntrials;
        double pmin = 0, pmax = 1;
        try {
            n = Integer.parseInt(args[0]);
            ntrials = Integer.parseInt(args[1]);
            if (args.length >= 3)
                pmin = Double.parseDouble(args[2]);
            if (args.length >= 4)
                pmax = Double.parseDouble(args[3]);
        }
        catch (Exception e)
        {
            System.out.println("usage: EvenlySpacedOnesTest N NTRIALS [pmin [pmax]]");
            System.exit(0);
            return; // make the compiler happy
        }

        final StatisticalSummary[] statistics;
        statistics=new StatisticalSummary[n+1];
        for (int i = 0; i <= n; ++i)
        {
            statistics[i] = new StatisticalSummary();
        }

        EvenlySpacedOnesTest test = new EvenlySpacedOnesTest(n, ntrials, pmin, pmax);
        int printInterval=100000;
        int nextPrint = printInterval;
        for (int i = 0; i < ntrials; ++i)
        {
            TestResult result = test.run();
            statistics[result.k].add(result.nsteps);
            if (i == nextPrint)
            {
                System.err.println(i);
                nextPrint += printInterval;
            }
        }
        StatisticalSummary.printOut(System.out, statistics);
    }
}

答案 17 :(得分:0)

显然我们需要至少同时检查一连串三胞胎,所以我们需要以某种方式压缩检查。我有一个候选算法,但分析时间复杂度超出了我的能力*时间阈值。

构建一个树,其中每个节点有三个子节点,每个节点包含其叶子的总数。建立一个1的链表。为每个节点分配一个与其覆盖的范围成比例的允许成本。只要我们在每个节点上花费的时间都在预算范围内,我们就会有一个O(n lg n)算法。

-

从根开始。如果它下面1的总数的平方小于其允许的成本,则应用朴素算法。否则就要对其子女进行报复。

现在我们已经在预算范围内退回,或者我们知道其中一个孩子中没有完全包含的有效三元组。因此,我们必须检查节点间三元组。

现在事情变得非常混乱。我们基本上想要在限制范围的同时对可能的儿童组进行递归。一旦范围受到足够的约束,天真算法将在预算范围内运行,您就可以完成。享受实施这一点,因为我保证这将是乏味的。有十几个案例。

-

我认为该算法有效的原因是因为没有有效三元组的序列似乎在1和0的序列之间交替。它有效地分割了附近的搜索空间,树模拟了这种分裂。

算法的运行时间根本不明显。它依赖于序列的非平凡属性。如果1非常稀疏,那么朴素算法将在预算范围内工作。如果1是密集的,那么应该立即找到匹配。但是如果密度“恰到好处”(例如,接近~n ^ 0.63,你可以通过在基数3中没有'2'数字的位置设置所有位来实现),我不知道它是否会起作用。你必须证明分裂效果足够强。

答案 18 :(得分:0)

一个简单的O(n)解决方案,O(n ^ 2)空间怎么样? (使用所有按位运算符在O(1)中工作的假设。)

该算法基本上分为四个阶段:

阶段1:对于原始数字中的每个位,找出它们的距离,但只考虑一个方向。 (我考虑了最低有效位方向的所有位。)

阶段2:反转输入中位的顺序;

第3阶段:在反向输入上重新运​​行第1步。

第4阶段:比较第1阶段和第3阶段的结果。如果任何位在上方和下方等间隔,我们必须有一个命中。

请记住,上述算法中的任何步骤都不会超过O(n)。 ^ _ ^

作为一个额外的好处,这个算法将从每个数字中找到所有等间隔的。因此,例如,如果您得到“0x0005”的结果,那么在1和3个单位之外有相等间隔的

我并没有真正尝试优化下面的代码,但它是可编辑的C#代码似乎有用。

using System;

namespace ThreeNumbers
{
    class Program
    {
        const int uint32Length = 32;

        static void Main(string[] args)
        {
            Console.Write("Please enter your integer: ");
            uint input = UInt32.Parse(Console.ReadLine());

            uint[] distancesLower = Distances(input);
            uint[] distancesHigher = Distances(Reverse(input));

            PrintHits(input, distancesLower, distancesHigher);
        }

        /// <summary>
        /// Returns an array showing how far the ones away from each bit in the input.  Only 
        /// considers ones at lower signifcant bits.  Index 0 represents the least significant bit 
        /// in the input.  Index 1 represents the second least significant bit in the input and so 
        /// on.  If a one is 3 away from the bit in question, then the third least significant bit 
        /// of the value will be sit.
        /// 
        /// As programed this algorithm needs: O(n) time, and O(n*log(n)) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        public static uint[] Distances(uint input)
        {
            uint[] distanceToOnes = new uint[uint32Length];
            uint result = 0;

            //Sets how far each bit is from other ones. Going in the direction of LSB to MSB
            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                distanceToOnes[arrayIndex] = result;
                result <<= 1;

                if ((input & bitIndex) != 0)
                {
                    result |= 1;
                }
            }

            return distanceToOnes;
        }

        /// <summary>
        /// Reverses the bits in the input.
        /// 
        /// As programmed this algorithm needs O(n) time and O(n) space.  
        /// (Where n is the number of bits in the input.)
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static uint Reverse(uint input)
        {
            uint reversedInput = 0;
            for (uint bitIndex = 1; bitIndex != 0; bitIndex <<= 1)
            {
                reversedInput <<= 1;
                reversedInput |= (uint)((input & bitIndex) != 0 ? 1 : 0);
            }

            return reversedInput;
        }

        /// <summary>
        /// Goes through each bit in the input, to check if there are any bits equally far away in 
        /// the distancesLower and distancesHigher
        /// </summary>
        public static void PrintHits(uint input, uint[] distancesLower, uint[] distancesHigher)
        {
            const int offset = uint32Length - 1;

            for (uint bitIndex = 1, arrayIndex = 0; bitIndex != 0; bitIndex <<= 1, ++arrayIndex)
            {
                //hits checks if any bits are equally spaced away from our current value
                bool isBitSet = (input & bitIndex) != 0;
                uint hits = distancesLower[arrayIndex] & distancesHigher[offset - arrayIndex];

                if (isBitSet && (hits != 0))
                {
                    Console.WriteLine(String.Format("The {0}-th LSB has hits 0x{1:x4} away", arrayIndex + 1, hits));
                }
            }
        }
    }
}

有人可能会评论说,对于任何足够大的数字,在O(1)中不能进行按位运算。你是对的。但是,我猜想每个使用加法,减法,乘法或除法的解法(不能通过移位来完成)也会产生这个问题。

答案 19 :(得分:0)

以下是一个解决方案。这里和那里可能会有一些小错误,但这个想法是合理的。

编辑:这不是n * log(n)

PSEUDO CODE:

foreach character in the string
  if the character equals 1 {         
     if length cache > 0 { //we can skip the first one
        foreach location in the cache { //last in first out kind of order
           if ((currentlocation + (currentlocation - location)) < length string)
              if (string[(currentlocation + (currentlocation - location))] equals 1)
                 return found evenly spaced string
           else
              break;
        }
     }
     remember the location of this character in a some sort of cache.
  }

return didn't find evenly spaced string

C#代码:

public static Boolean FindThreeEvenlySpacedOnes(String str) {
    List<int> cache = new List<int>();

    for (var x = 0; x < str.Length; x++) {
        if (str[x] == '1') {
            if (cache.Count > 0) {
                for (var i = cache.Count - 1; i > 0; i--) {
                    if ((x + (x - cache[i])) >= str.Length)
                        break;

                    if (str[(x + (x - cache[i]))] == '1')
                        return true;                            
                }
            }
            cache.Add(x);                    
        }
    }

    return false;
}

工作原理:

iteration 1:
x
|
101101001
// the location of this 1 is stored in the cache

iteration 2:
 x
 | 
101101001

iteration 3:
a x b 
| | | 
101101001
//we retrieve location a out of the cache and then based on a 
//we calculate b and check if te string contains a 1 on location b

//and of course we store x in the cache because it's a 1

iteration 4:
  axb  
  |||  
101101001

a  x  b  
|  |  |  
101101001


iteration 5:
    x  
    |  
101101001

iteration 6:
   a x b 
   | | | 
101101001

  a  x  b 
  |  |  | 
101101001
//return found evenly spaced string

答案 20 :(得分:0)

我会试着提出一种数学方法。这是一个开始而不是结束,所以任何帮助,评论,甚至矛盾 - 都将深受赞赏。但是,如果这种方法得到证实 - 该算法是字符串中的直接搜索。

  1. 给定固定数量的空格k和字符串S,搜索k-spaced-triplet需要O(n) - 我们只测试每个{{1}如果0<=i<=(n-2k)。测试需要S[i]==S[i+k]==S[i+2k]我们执行O(1)n-k为常量,因此需要k

  2. 让我们假设O(n-k)=O(n)的数量与我们需要搜索的最大空格之间存在反比例。也就是说,如果有很多1,那么必须有一个三元组,它必须非常密集;如果只有少量1,那么三元组(如果有的话)可能非常稀疏。换句话说,我可以证明,如果我有足够的1,那么必须存在这样的三元组 - 并且我拥有的1越多,就必须找到更密集的三元组。这可以通过Pigeonhole principle来解释 - 希望稍后详细说明。

  3. 假设我必须查找可能的空格数上限1。现在,对于位于k的每个1,我们需要在S[i]1S[i-1]S[i+1]中检查S[i-2] ,... S[i+2]S[i-k]。对于S[i+k]中的每个O((k^2-k)/2)=O(k^2),这需要1 - 由于Gauss' Series Summation Formula。请注意,这与第1部分不同 - 我将S作为空格数的上限,而不是作为常量空间。

  4. 我们需要证明k。也就是说,我们需要证明O(n*log(n))k*(number of 1's)成正比。

    如果我们可以做到这一点,算法很简单 - 对于索引为log(n)的{​​{1}}中的每个1,只需从每个方向查找S距离i。如果在相同距离内找到两个,请返回1k。同样,棘手的部分是找到i并证明其正确性。

    我非常感谢您的评论 - 我一直试图在我的白板上找到kk的数量之间的关系,到目前为止还没有成功。

答案 21 :(得分:0)

我认为在发布问题的第22个天真解决方案之前我会添加一条评论。对于天真的解决方案,我们不需要显示字符串中1的数量最多为O(log(n)),而是最多为O(sqrt(n * log(n))。

求解:

def solve(Str):
    indexes=[]
    #O(n) setup
    for i in range(len(Str)):
        if Str[i]=='1':
            indexes.append(i)

    #O((number of 1's)^2) processing
    for i in range(len(indexes)):
        for j in range(i+1, len(indexes)):
                            indexDiff = indexes[j] - indexes[i]
            k=indexes[j] + indexDiff
            if k<len(Str) and Str[k]=='1':
                return True
    return False

这基本上类似于flybywire的想法和实现,虽然展望未来而不是回头。

Greedy String Builder:

#assumes final char hasn't been added, and would be a 1 
def lastCharMakesSolvable(Str):
    endIndex=len(Str)
    j=endIndex-1
    while j-(endIndex-j) >= 0:
        k=j-(endIndex-j)
        if k >= 0 and Str[k]=='1' and Str[j]=='1':
            return True
        j=j-1
    return False



def expandString(StartString=''):
    if lastCharMakesSolvable(StartString):
        return StartString + '0'
    return StartString + '1'

n=1
BaseStr=""
lastCount=0
while n<1000000:
    BaseStr=expandString(BaseStr)
    count=BaseStr.count('1')
    if count != lastCount:
        print(len(BaseStr), count)
    lastCount=count
    n=n+1

(在我的辩护中,我仍处于'学习python'阶段的理解中)

此外,从贪婪的弦乐建筑中可能有用的输出,在1的数量达到2的幂之后有一个相当一致的跳跃......我不愿意等到见证击中2096.

strlength   # of 1's
    1    1
    2    2
    4    3
    5    4
   10    5
   14    8
   28    9
   41    16
   82    17
  122    32
  244    33
  365    64
  730    65
 1094    128
 2188    129
 3281    256
 6562    257
 9842    512
19684    513
29525    1024

答案 22 :(得分:0)

以下是一些想法,尽管我付出了最大的努力,但似乎不会陷入困境。不过,它们可能是某人分析的有用起点。

考虑所提出的解决方案如下,这是几个人建议的方法,包括我自己在这个答案的先前版本中。 :)

  1. 修剪前导和尾随零。
  2. 扫描字符串寻找1。
  3. 找到1时:
    1. 假设它是解决方案的中间部分。
    2. 对于每个先前的1,使用其保存的位置来计算最终1的预期位置。
    3. 如果计算出的位置在字符串结尾之后,则它不能成为解决方案的一部分,所以从候选列表中删除该位置。
    4. 检查解决方案。
  4. 如果未找到解决方案,请将当前1添加到候选列表中。
  5. 重复直到找不到1个。
  6. 现在考虑输入字符串字符串,如下所示,它们没有解决方案:

    101
    101001
    1010010001
    101001000100001
    101001000100001000001
    

    一般来说,这是形式为j 0的k个字符串的串联,后面跟着从0到k-1的j为1。

    k=2  101
    k=3  101001
    k=4  1010010001
    k=5  101001000100001
    k=6  101001000100001000001
    

    请注意,子串的长度为1,2,3等。因此,问题大小n具有长度为1到k的子串,使得n = k(k + 1)/ 2.

    k=2  n= 3  101
    k=3  n= 6  101001
    k=4  n=10  1010010001
    k=5  n=15  101001000100001
    k=6  n=21  101001000100001000001
    

    请注意,k还会跟踪我们必须考虑的1的数量。请记住,每当我们看到1时,我们需要考虑到目前为止看到的所有1。因此,当我们看到第二个1时,我们只考虑第一个,当我们看到第三个1时,我们重新考虑前两个,当我们看到第四个1时,我们需要重新考虑前三个,依此类推。到算法结束时,我们考虑了k(k-1)/ 2对1。打电话给那个。

    k=2  n= 3  p= 1  101
    k=3  n= 6  p= 3  101001
    k=4  n=10  p= 6  1010010001
    k=5  n=15  p=10  101001000100001
    k=6  n=21  p=15  101001000100001000001
    

    n和p之间的关系是n = p + k。

    遍历字符串的过程需要O(n)时间。每次遇到1时,最多进行(k-1)次比较。由于n = k(k + 1)/ 2,n> 1。 k ** 2,所以sqrt(n)> ķ。这给了我们O(n sqrt(n))或O(n ** 3/2)。但请注意,这可能不是一个非常严格的界限,因为比较的数量从1到最大值为k,它不是整个时间的k。但我不确定如何在数学中考虑到这一点。

    它仍然不是O(n log(n))。此外,我无法证明这些输入是最糟糕的情况,尽管我怀疑它们是。我认为在前面加一个更密集的包装会导致最后的包装更加稀疏。

    由于有人可能仍然觉得它很有用,这里是Perl中该解决方案的代码:

    #!/usr/bin/perl
    
    # read input as first argument
    my $s = $ARGV[0];
    
    # validate the input
    $s =~ /^[01]+$/ or die "invalid input string\n";
    
    # strip leading and trailing 0's
    $s =~ s/^0+//;
    $s =~ s/0+$//;
    
    # prime the position list with the first '1' at position 0
    my @p = (0);
    
    # start at position 1, which is the second character
    my $i = 1;
    
    print "the string is $s\n\n";
    
    while ($i < length($s)) {
       if (substr($s, $i, 1) eq '1') {
          print "found '1' at position $i\n";
          my @t = ();
          # assuming this is the middle '1', go through the positions
          # of all the prior '1's and check whether there's another '1'
          # in the correct position after this '1' to make a solution
          while (scalar @p) {
             # $p is the position of the prior '1'
             my $p = shift @p;
             # $j is the corresponding position for the following '1'
             my $j = 2 * $i - $p;
             # if $j is off the end of the string then we don't need to
             # check $p anymore
             next if ($j >= length($s));
             print "checking positions $p, $i, $j\n";
             if (substr($s, $j, 1) eq '1') {
                print "\nsolution found at positions $p, $i, $j\n";
                exit 0;
             }
             # if $j isn't off the end of the string, keep $p for next time
             push @t, $p;
          }
          @p = @t;
          # add this '1' to the list of '1' positions
          push @p, $i;
       }
       $i++;
    }
    
    print "\nno solution found\n";
    

答案 23 :(得分:0)

扫描1时,将其位置添加到列表中。添加第二个和连续的1时,将它们与列表中的每个位置进行比较。间距等于currentOne(center) - previousOne(左)。右侧位是currentOne +间距。如果它是1,那么结束。

一个列表与它们之间的空间成反比。简单地说,如果你在1之间有很多0(在最坏的情况下),你的已知1的列表将会非常缓慢地增长。

using System;
using System.Collections.Generic;

namespace spacedOnes
{
    class Program
    {
        static int[] _bits = new int[8] {128, 64, 32, 16, 8, 4, 2, 1};

        static void Main(string[] args)
        {
            var bytes = new byte[4];
            var r = new Random();
            r.NextBytes(bytes);
            foreach (var b in bytes) {
                Console.Write(getByteString(b));
            }
            Console.WriteLine();
            var bitCount = bytes.Length * 8;
            var done = false;
            var onePositions = new List<int>();
            for (var i = 0; i < bitCount; i++)
            {
                if (isOne(bytes, i)) {
                    if (onePositions.Count > 0) {
                        foreach (var knownOne in onePositions) {
                            var spacing = i - knownOne;
                            var k = i + spacing;
                            if (k < bitCount && isOne(bytes, k)) {
                                Console.WriteLine("^".PadLeft(knownOne + 1) + "^".PadLeft(spacing) + "^".PadLeft(spacing));
                                done = true;
                                break;
                            }
                        }
                    }
                    if (done) {
                        break;
                    }
                    onePositions.Add(i);
                }
            }
            Console.ReadKey();
        }

        static String getByteString(byte b) {
            var s = new char[8];
            for (var i=0; i<s.Length; i++) {
                s[i] = ((b & _bits[i]) > 0 ? '1' : '0');
            }
            return new String(s);
        }

        static bool isOne(byte[] bytes, int i)
        {
            var byteIndex = i / 8;
            var bitIndex = i % 8;
            return (bytes[byteIndex] & _bits[bitIndex]) > 0;
        }
    }
}

答案 24 :(得分:0)

假设:

错了,谈论log(n)上限数

修改

现在我发现使用Cantor数字(如果正确),集合上的密度是(2/3)^ Log_3(n)(这是一个奇怪的函数)并且我同意,log(n)/ n密度是强的。

如果这是上限,有算法至少在O(n *(3/2)^(log(n)/ log(3)))时间复杂度和O((3/2)中解决这个问题)^(log(n)/ log(3)))空间复杂度。 (检查Justice对algorhitm的回答)

这仍远远优于O(n ^ 2)

这个函数((3/2)^(log(n)/ log(3)))看起来真的像n * log(n)。

我是如何得到这个公式的?

在字符串上放置Cantor编号。
假设字符串的长度是3 ^ p == n
在生成Cantor字符串的每一步中,您都会保留2/3的流行数量。适用这两次。

那意味着(n *((2/3)^ p)) - &gt; (((3 ^ p))*((2/3)^ p))其余的并且在简化后2 ^ p。 这意味着3 ^ p字符串中的2 ^ p个 - &gt; (3/2)^ p个。替换p = log(n)/ log(3)并得到
((3/2)^(log(n)/ log(3)))

答案 25 :(得分:0)

我认为这是nlog(n)的原因是由于以下原因:

  • 要查找作为三元组开头的1,您需要检查(n-2)个字符。如果你还没有找到它,你就不会(字符n-1和n不能启动三元组)(O(n))
  • 要查找作为三元组一部分的第二个1(由第一个开始),您需要检查m / 2(m = n-x,其中x是前1个的偏移量)字符。这是因为,如果你从第一个到最后一半时没有找到第二个1,你就不会......因为第三个1必须与第二个相距完全相同。 (O(的log(n)))
  • O(1)找到最后一个,因为你知道它找到第一个和第二个时必须的索引。

所以,你有n,log(n)和1 ... O(nlogn)

编辑:哎呀,我的坏人。我的大脑已经确定n / 2是logn ...它显然不是(项目数量加倍仍然是内循环迭代次数的两倍)。这仍然是n ^ 2,没有解决问题。好吧,至少我得写一些代码:)


在Tcl中实施

proc get-triplet {input} {
    for {set first 0} {$first < [string length $input]-2} {incr first} {
        if {[string index $input $first] != 1} {
            continue
        }
        set start [expr {$first + 1}]
        set end [expr {1+ $first + (([string length $input] - $first) /2)}]
        for {set second $start} {$second < $end} {incr second} {
            if {[string index $input $second] != 1} {
                continue
            }
            set last [expr {($second - $first) + $second}]
            if {[string index $input $last] == 1} {
                return [list $first $second $last]
            }
        }
    }
    return {}
}

get-triplet 10101      ;# 0 2 4
get-triplet 10111      ;# 0 2 4
get-triplet 11100000   ;# 0 1 2
get-triplet 0100100100 ;# 1 4 7

答案 26 :(得分:0)

我想我找到了一种解决问题的方法,但我无法构建一个正式的证据。我所做的解决方案是用Java编写的,它使用计数器'n'来计算它执行的列表/数组访问量。因此,如果正确,则n应小于或等于stringLength * log(stringLength)。我尝试了数字0到2 ^ 22,它可以工作。

首先迭代输入字符串,然后列出包含一个索引的所有索引。这只是O(n)。

然后从索引列表中选择firstIndex,并使用大于第一个索引的secondIndex。这两个索引必须包含索引,因为它们位于索引列表中。从那里可以计算出thirdIndex。如果inputString [thirdIndex]为1则停止。

public static int testString(String input){
//n is the number of array/list accesses in the algorithm
int n=0;

//Put the indices of all the ones into a list, O(n)
ArrayList<Integer> ones = new ArrayList<Integer>();
for(int i=0;i<input.length();i++){
    if(input.charAt(i)=='1'){
        ones.add(i);
    }
}

//If less than three ones in list, just stop
if(ones.size()<3){
    return n;
}

int firstIndex, secondIndex, thirdIndex;
for(int x=0;x<ones.size()-2;x++){
    n++;
    firstIndex = ones.get(x);

    for(int y=x+1; y<ones.size()-1; y++){
        n++;
        secondIndex = ones.get(y);
        thirdIndex = secondIndex*2 - firstIndex;

        if(thirdIndex >= input.length()){
            break;
        }

        n++;
        if(input.charAt(thirdIndex) == '1'){
            //This case is satisfied if it has found three evenly spaced ones
            //System.out.println("This one => " + input);
            return n;
        }
    }
}

return n;

}

附加说明:当迭代输入字符串以构造索引列表时,计数器n不会递增。此操作为O(n),因此无论如何它都不会对算法复杂性产生影响。

答案 27 :(得分:-2)

您可以使用Rabin-Karp算法进行改编。 它的复杂性为0(n),因此它可以帮助你。

看看http://en.wikipedia.org/wiki/Rabin-Karp_string_search_algorithm

答案 28 :(得分:-3)

这可能是一个解决方案吗?我不确定它是否是O(nlogn),但在我看来它比O(n²)更好,因为找不到三元组的唯一方法就是素数分布。

还有改进的余地,第二个发现1可能是下一个1.也没有错误检查。

#include <iostream>

#include <string>

int findIt(std::string toCheck) {
    for (int i=0; i<toCheck.length(); i++) {
        if (toCheck[i]=='1') {
            std::cout << i << ": " << toCheck[i];
            for (int j = i+1; j<toCheck.length(); j++) {
                if (toCheck[j]=='1' && toCheck[(i+2*(j-i))] == '1') {
                    std::cout << ", " << j << ":" << toCheck[j] << ", " << (i+2*(j-i)) << ":" << toCheck[(i+2*(j-i))] << "    found" << std::endl;
                    return 0;
                }
            }
        }
    }
    return -1;
}

int main (int agrc, char* args[]) {
    std::string toCheck("1001011");
    findIt(toCheck);
    std::cin.get();
    return 0;
}

答案 29 :(得分:-3)

我认为这个算法具有O(n log n)复杂度(C ++,DevStudio 2k5)。现在,我不知道如何分析算法以确定其复杂性的细节,因此我在代码中添加了一些度量收集信息。对于任何给定的输入,代码计算在1和0的序列上完成的测试的数量(希望,我没有做出算法的球)。我们可以将实际测试次数与O值进行比较,看看是否存在相关性。

#include <iostream>
using namespace std;

bool HasEvenBits (string &sequence, int &num_compares)
{
  bool
    has_even_bits = false;

  num_compares = 0;

  for (unsigned i = 1 ; i <= (sequence.length () - 1) / 2 ; ++i)
  {
    for (unsigned j = 0 ; j < sequence.length () - 2 * i ; ++j)
    {
      ++num_compares;
      if (sequence [j] == '1' && sequence [j + i] == '1' && sequence [j + i * 2] == '1')
      {
        has_even_bits = true;
        // we could 'break' here, but I want to know the worst case scenario so keep going to the end
      }
    }
  }

  return has_even_bits;
}

int main ()
{
  int
    count;

  string
    input = "111";

  for (int i = 3 ; i < 32 ; ++i)
  {
    HasEvenBits (input, count);
    cout << i << ", " << count << endl;
    input += "0";
  }
}

该程序输出每个字符串长度最多32个字符的测试次数。结果如下:

 n  Tests  n log (n)
=====================
 3     1     1.43
 4     2     2.41
 5     4     3.49
 6     6     4.67
 7     9     5.92
 8    12     7.22
 9    16     8.59
10    20    10.00
11    25    11.46
12    30    12.95
13    36    14.48
14    42    16.05
15    49    17.64
16    56    19.27
17    64    20.92
18    72    22.59
19    81    24.30
20    90    26.02
21   100    27.77
22   110    29.53
23   121    31.32
24   132    33.13
25   144    34.95
26   156    36.79
27   169    38.65
28   182    40.52
29   196    42.41
30   210    44.31
31   225    46.23

我也添加了'n log n'值。使用您选择的绘图工具绘制这些图形,以查看两个结果之间的相关性。此分析是否扩展到n的所有值?我不知道。

答案 30 :(得分:-7)

这可以在线性时间O(n)

中求解
  1. 从头开始,等待第一个
  2. 开始计算零。
  3. 当您点击1个商店的计数零数时(有效数字也为0) NumberOfZeros - &gt; PrevZeros
  4. 开始计数零。
  5. 点击1后,检查 NumberOfZeros == PrevZeros

    如果为true 返回计数器

    其他 NumberOfZeros - &gt; prev_zeros 转到4