Подтвердить что ты не робот

Пространственно-эффективный алгоритм для нахождения наибольшего сбалансированного подмассива?

заданный массив из 0s и 1s, найдите максимальный субарей, такой, что число нулей и 1s равно. Это нужно сделать в O (n) времени и O (1) пространстве.

У меня есть algo, который делает это в O (n) времени и O (n) пространстве. Он использует префикс sum array и использует тот факт, что если число 0s и 1s одинаково, тогда sumOfSubarray = lengthOfSubarray/2

#include<iostream>
#define M 15

using namespace std;

void getSum(int arr[],int prefixsum[],int size) {
    int i;
    prefixsum[0]=arr[0]=0;
    prefixsum[1]=arr[1];
    for (i=2;i<=size;i++) {
        prefixsum[i]=prefixsum[i-1]+arr[i];
    }
}

void find(int a[],int &start,int &end) {
    while(start < end) {
        int mid = (start +end )/2;
        if((end-start+1) == 2 * (a[end] - a[start-1]))
                break;
        if((end-start+1) > 2 * (a[end] - a[start-1])) {
            if(a[start]==0 && a[end]==1)
                    start++; else
                    end--;
        } else {
            if(a[start]==1 && a[end]==0)
                    start++; else
                    end--;
        }
    }
}

int main() {
    int size,arr[M],ps[M],start=1,end,width;
    ;
    cin>>size;
    arr[0]=0;
    end=size;
    for (int i=1;i<=size;i++)
            cin>>arr[i];
    getSum(arr,ps,size);
    find(ps,start,end);
    if(start!=end)
            cout<<(start-1)<<" "<<(end-1)<<endl; else cout<<"No soln\n";
    return 0;
}
4b9b3361

Ответ 1

Теперь мой алгоритм - это O (n) время и O (Dn) пространство, где Dn - полное вложение в список.

Это решение не изменяет список.

пусть D - разность 1s и 0s, найденных в списке.

Сначала дайте шаг линейно через список и вычислите D, чтобы увидеть, как он работает:

Я собираюсь использовать этот список в качестве примера: l = 1100111100001110

Element   D
null      0
1         1
1         2   <-
0         1
0         0
1         1
1         2
1         3
1         4
0         3
0         2
0         1
0         0
1         1
1         2
1         3
0         2   <-

Поиск самого длинного сбалансированного субарара эквивалентен поиску 2 равных элементов в D, которые являются более далекими. (в этом примере 2 2s, отмеченные стрелками.)

Самый длинный сбалансированный подмассив между первым входом элемента +1 и последним элементом элемента. (первая стрелка +1 и последняя стрелка: 00111100001110)

Примечание:

Самый длинный подмассив всегда будет находиться между двумя элементами D, которые между [0, Dn], где Dn - последний элемент D. (Dn = 2 в предыдущий пример) Dn - это полный дисбаланс между 1s и 0s в список. (или [Dn, 0], если Dn отрицательна)

В этом примере это означает, что мне не нужно "смотреть" на 3s или 4s

Доказательство:

Пусть Dn > 0.

Если существует подматрица, ограниченная P (P > Dn). Так как 0 < Dn < П, до достижения первого элемента D, равного P, мы достигаем одного элемент, равный Dn. Таким образом, поскольку последний элемент списка равен Dn, существует длинный субарей, ограниченный Dns, чем тот, который ограничен Ps.And поэтому нам не нужно смотреть на Ps

P не может быть меньше 0 по тем же причинам

доказательство одно и то же для Dn < 0

Теперь пусть работа над D, D не является случайной, разница между двумя последовательными элементами всегда равна 1 или -1. Ans есть простая биекция между D и исходным списком. Поэтому у меня есть 2 решения этой проблемы:

  • первым из них является отслеживание первого и последнего появления каждого элемент в D, который находится между 0 и Dn (примечание cf).
  • second - преобразовать список в D, а затем работать с D.

ПЕРВЫЙ РЕШЕНИЕ

В настоящее время я не могу найти лучший подход, чем первый:

Сначала вычислим Dn (в O (n)). Дн = 2

Второй вместо создания D создайте dictionnary, где ключи - это значение D (между [0 и Dn]), а значение каждого ключа - пара (a, b), где a - первое появление и b последний.

Element   D DICTIONNARY
null      0 {0:(0,0)}
1         1 {0:(0,0) 1:(1,1)}
1         2 {0:(0,0) 1:(1,1) 2:(2,2)}
0         1 {0:(0,0) 1:(1,3) 2:(2,2)}
0         0 {0:(0,4) 1:(1,3) 2:(2,2)}
1         1 {0:(0,4) 1:(1,5) 2:(2,2)}
1         2 {0:(0,4) 1:(1,5) 2:(2,6)}
1         3 { 0:(0,4) 1:(1,5) 2:(2,6)}
1         4 {0:(0,4) 1:(1,5) 2:(2,6)}  
0         3{0:(0,4) 1:(1,5) 2:(2,6) }
0         2 {0:(0,4) 1:(1,5) 2:(2,9) }
0         1 {0:(0,4) 1:(1,10) 2:(2,9) } 
0         0 {0:(0,11) 1:(1,10) 2:(2,9) } 
1         1 {0:(0,11) 1:(1,12) 2:(2,9) } 
1         2 {0:(0,11) 1:(1,12) 2:(2,13)}
1         3 {0:(0,11) 1:(1,12) 2:(2,13)} 
0         2 {0:(0,11) 1:(1,12) 2:(2,15)} 

и вы выбрали элемент с наибольшей разницей: 2: (2,15) и l [3:15] = 00111100001110 (с l = 1100111100001110).

Сложность времени:

2 проходит, первый - для каплирования Dn, второй - для создания dictionnary. найти макс в словаре.

Всего O (n)

Сложность пространства:

текущий элемент в D: O (1), dictionnary O (Dn)

Я не беру 3 и 4 словаря из-за замечания

Сложность - это O (n) время и O (Dn) пространство (в среднем случае Dn < п).

Я думаю, что для такого подхода может быть лучший способ, чем словарь.

Любое предложение приветствуется.

Надеюсь, что это поможет


ВТОРОЕ РЕШЕНИЕ (ТОЛЬКО ИДЕЯ НЕ РЕАЛЬНОЕ РЕШЕНИЕ)

Второй способ продолжения - преобразовать ваш список в D. (так как легко вернуться из D в список, это нормально). (O (n) и O (1) пространство, так как я преобразовываю список на место, даже если он не может быть "действительным" O (1))

Затем из D вам нужно найти 2 равных элемента, которые являются более далекими.

похоже на поиск самого длинного цикла в связанном списке, модификация алгоритма Ричарда Брента может вернуть самый длинный цикл, но я не знать, как это сделать, и это займет время O (n) и O (1).

Как только вы найдете самый длинный цикл, вернитесь к первому списку и распечатайте его.

Этот алгоритм займет время O (n) и сложность пространства O (1).

Ответ 2

Разный подход, но все же O (n) время и память. Начните с предложения Neil, обработайте 0 как -1.

Обозначение: A[0, …, N-1] - ваш массив размера N, f(0)=0, f(x)=A[x-1]+f(x-1) - функция

Если вы нарисуете f, вы увидите, что то, что вы ищете, это точки, для которых f(m)=f(n), m=n-2k где k-положительный естественный. Точнее, только для x таких, что A[x]!=A[x+1] (и последний элемент в массиве) вы должны проверить, произошло ли уже f(x). К сожалению, теперь я не вижу улучшения по сравнению с массивом B[-N+1…N-1], где такая информация будет сохранена.

Чтобы завершить мою мысль: B[x]=-1 изначально, B[x]=p, когда p = min k: f(k)=x. И алгоритм (дважды проверьте его, поскольку я очень устал):

fx = 0
B = new array[-N+1, …, N-1]
maxlen = 0
B[0]=0
for i=1…N-1 :
    fx = fx + A[i-1]
    if B[fx]==-1 :
        B[fx]=i
    else if ((i==N-1) or (A[i-1]!=A[i])) and (maxlen < i-B[fx]):
        We found that A[B[fx], …, i] is best than what we found so far
        maxlen = i-B[fx]

Изменить: две кровати-мысли (= разобрались во время укладки в постели: P):

1). Вы можете выполнить двоичный поиск результата по длине подмассива, что даст время O (n log n) и O (1) алгоритм памяти. Пусть используется функция g(x)=x - x mod 2 (поскольку подмассивы, сумма которых равна 0, всегда имеют четную длину). Начните с проверки, если весь массив равен 0. Если да - мы закончили, в противном случае продолжаем. Предположим теперь, что 0 - начальная точка (мы знаем, что такой подрамник такой длины и "свойство суммирования к нулю" ) и g (N-1) как конечная точка (мы не знаем, что такого подмассива нет). Пусть do

    a = 0
    b = g(N-1)
    while a<b : 
        c = g((a+b)/2)
        check if there is such subarray in O(n) time
        if yes:
            a = c
        if no:
            b = c
    return the result: a (length of maximum subarray)

Проверка субарара с "суммированием на нуль" некоторой заданной длины L проста:

    a = 0
    b = L
    fa = fb = 0
    for i=0…L-1:
        fb = fb + A[i]
    while (fa != fb) and (b<N) :
        fa = fa + A[a]
        fb = fb + A[b]
        a = a + 1
        b = b + 1
    if b==N:
        not found
    found, starts at a and stops at b

2)... можете ли вы изменить входной массив? Если да и если O (1) память означает точно, что вы не используете дополнительное пространство (за исключением постоянного количества элементов), просто сохраните значения префиксной таблицы в своем входном массиве. Больше места не используется (за исключением некоторых переменных): D

И снова, дважды проверьте мои алгоритмы, поскольку я устал от усталости и мог бы делать ошибки за один раз.

Ответ 3

Как и Нейл, мне полезно рассмотреть алфавит {± 1} вместо {0, 1}. Предположим без потери общности, что по крайней мере столько же + 1s, сколько -1s. Следующий алгоритм, который использует бит O (sqrt (n log n)) и запускается во времени O (n), обусловлен "A.F."

Примечание. Это решение не обманывает, предполагая, что вход модифицируется и/или потерял бит. Начиная с этого редактирования это решение является единственным опубликованным, которое представляет собой O (n) время и o (n) пространство.

Более простая версия, использующая биты O (n), передает массив префиксных сумм и отмечает первое вхождение каждого значения. Затем он сканирует назад, рассматривая для каждой высоты между 0 и суммой (arr) максимальный подмассив на этой высоте. Некоторые мысли показывают, что среди них есть оптимальный (помните предположение). В Python:

sum = 0
min_so_far = 0
max_so_far = 0
is_first = [True] * (1 + len(arr))
for i, x in enumerate(arr):
    sum += x
    if sum < min_so_far:
        min_so_far = sum
    elif sum > max_so_far:
        max_so_far = sum
    else:
        is_first[1 + i] = False

sum_i = 0
i = 0
while sum_i != sum:
    sum_i += arr[i]
    i += 1
sum_j = sum
j = len(arr)
longest = j - i
for h in xrange(sum - 1, -1, -1):
    while sum_i != h or not is_first[i]:
        i -= 1
        sum_i -= arr[i]
    while sum_j != h:
        j -= 1
        sum_j -= arr[j]
    longest = max(longest, j - i)

Трюк, чтобы получить пространство вниз, приходит из замечаний о том, что мы сканируем is_first последовательно, хотя и в обратном порядке относительно его построения. Поскольку переменные цикла соответствуют битам O (log n), мы будем вычислять вместо is_first контрольную точку переменных цикла после каждого шага O (√ (n log n)). Это O (n/√ (n log n)) = O (√ (n/log n)) контрольные точки для всего O (√ (n log n)) бит. Путем перезапуска цикла с контрольной точки мы вычисляем по требованию каждую O (√ (n log n)) - разрядную секцию is_first.

(PS: it может быть или не быть моей ошибкой, что оператор проблемы запрашивает O (1) пространство. Я искренне извиняюсь, если это был я, который вытащил Fermat и предположил, что у меня было решение проблемы намного сложнее, чем я думал.)

Ответ 4

Если ваш алгоритм действительно во всех случаях (см. мой комментарий к вашему вопросу, отметив некоторые исправления), обратите внимание, что префиксный массив является единственным препятствием для вашей постоянной цели памяти.

Изучение функции find показывает, что этот массив можно заменить двумя целыми числами, тем самым устраняя зависимость от длины ввода и решения вашей проблемы. Рассмотрим следующее:

  • Вы можете использовать только два значения в массиве префикса в функции find. Это a[start - 1] и a[end]. Да, start и end изменяются, но заслуживает ли это массив?
  • Посмотрите на ход вашей петли. В конце start увеличивается или end уменьшается только одним.
  • Учитывая предыдущий оператор, если вы хотите заменить значение a[start - 1] на целое число, как бы вы обновили его значение? Иными словами, для каждого перехода в цикле, который меняет значение start, что вы можете сделать, чтобы обновить целое число соответственно, чтобы отразить новое значение a[start - 1]?
  • Можно ли повторить этот процесс с помощью a[end]?
  • Если на самом деле значения a[start - 1] и a[end] могут быть отражены двумя целыми числами, не весь ли префиксный массив больше не служит цели? Не может ли быть удалено?

При отсутствии необходимости в префиксном массиве и всех зависимостях хранения от длины ввода, ваш алгоритм будет использовать постоянный объем памяти для достижения своей цели, тем самым делая это время O (n) и O (1).

Я бы предпочел, чтобы вы решили это самостоятельно, основываясь на проницательности выше, поскольку это домашнее задание. Тем не менее, я привел решение ниже для справки:

#include <iostream>
using namespace std;

void find( int *data, int &start, int &end )
{
    // reflects the prefix sum until start - 1
    int sumStart = 0;

    // reflects the prefix sum until end
    int sumEnd = 0;
    for( int i = start; i <= end; i++ )
        sumEnd += data[i];

    while( start < end )
    {
        int length = end - start + 1;
        int sum = 2 * ( sumEnd - sumStart );

        if( sum == length )
            break;
        else if( sum < length )
        {
            // sum needs to increase; get rid of the lower endpoint
            if( data[ start ] == 0 && data[ end ] == 1 )
            {
                // sumStart must be updated to reflect the new prefix sum
                sumStart += data[ start ];
                start++;
            }
            else
            {
                // sumEnd must be updated to reflect the new prefix sum
                sumEnd -= data[ end ];
                end--;
            }
        }
        else
        {
            // sum needs to decrease; get rid of the higher endpoint
            if( data[ start ] == 1 && data[ end ] == 0 )
            {
                // sumStart must be updated to reflect the new prefix sum
                sumStart += data[ start ];
                start++;
            }
            else
            {
                // sumEnd must be updated to reflect the new prefix sum
                sumEnd -= data[ end ];
                end--;
            }
        }
    }
}

int main() {
    int length;
    cin >> length;

    // get the data
    int data[length];
    for( int i = 0; i < length; i++ )
        cin >> data[i];

    // solve and print the solution
    int start = 0, end = length - 1;
    find( data, start, end );

    if( start == end )
        puts( "No soln" );
    else
        printf( "%d %d\n", start, end );

    return 0;
}

Ответ 5

Этот алгоритм представляет собой O (n) время и O (1) пространство. Он может изменять исходный массив, но он восстанавливает всю информацию обратно. Таким образом, он не работает с константными массивами. Если эта головоломка имеет несколько решений, этот алгоритм выбирает решение, ближайшее к началу массива. Или он может быть изменен для обеспечения всех решений.

Алгоритм

Переменные

  • p1 - начало подмаскировки
  • p2 - конец подмассива
  • d - разность 1s и 0s в подмассиве

    • Рассчитайте d, если d==0, остановите. Если d<0, инвертируйте массив и после того, как сбалансированный субарр обнаружен, верните его обратно.
    • Пока d > 0 advance p2: если элемент массива равен 1, просто уменьшите как p2, так и d. В противном случае p2 должен передать подмассиву формы 11*0, где * - некоторый сбалансированный подмассива. Чтобы сделать возможной обратную трассировку, 11*0? изменяется на 0?*00 (где ? - это значение рядом с подмассивом). Тогда d уменьшается.
    • Сохранить p1 и p2.
    • Backtrack p2: если элемент массива равен 1, просто увеличивайте p2. В противном случае мы нашли элемент, измененный на шаге 2. Отмените изменения и передайте подмашину формы 11*0.
    • Advance p1: если элемент массива равен 1, просто увеличивайте p1. В противном случае p1 должен передать подмашину формы 0*11.
    • Сохранить p1 и p2, если p2 - p1 улучшено.
    • Если p2 находится в конце массива, остановитесь. В противном случае перейдите к шагу 4.

enter image description here

Как это работает

Алгоритм выполняет итерацию через все возможные положения сбалансированного подмассива во входном массиве. Для каждой позиции подмашины p1 и p2 поддерживаются как можно дальше друг от друга, обеспечивая локально длинный подмассива. Subarray с максимальной длиной выбирается между всеми этими подмассивами.

Чтобы определить следующую лучшую позицию для p1, она переместится в первую позицию, где баланс между 1 и 0 изменяется на единицу. (Шаг 5).

Чтобы определить следующую лучшую позицию для p2, она переместится в последнюю позицию, где баланс между 1 и 0 изменяется на единицу. Чтобы это стало возможным, шаг 2 обнаруживает все такие позиции (начиная с конца массива) и модифицирует массив таким образом, что можно выполнять итерацию этих позиций с помощью линейного поиска. (Шаг 4).

Выполняя шаг 2, можно выполнить два возможных условия. Простой: когда найдено значение "1" ; указатель p2 просто доведен до следующего значения, никакого специального лечения не требуется. Но когда найдено значение "0" , баланс идет в неправильном направлении, необходимо пройти через несколько бит, пока не будет найден правильный баланс. Все эти биты не представляют интереса к алгоритму, при остановке p2 будет либо сбалансированный субарей, слишком короткий, либо несогласованный подмассива. В результате p2 должен передать подмассиву формы 11*0 (справа налево, * означает любой сбалансированный подмассиво). Нет никакого шанса пойти одинаково в другом направлении. Но можно временно использовать некоторые биты из шаблона 11*0, чтобы можно было вернуться назад. Если мы изменим первый "1" на "0" , второй "1" на значение рядом с самым правым "0" и очистим значение рядом с самой правой "0" : 11*0? -> 0?*00, тогда мы получим возможность ( сначала) обратите внимание на шаблон на обратном пути, так как он начинается с "0" и (второй) находит следующую хорошую позицию для p2.

Код С++:

#include <cstddef>
#include <bitset>

static const size_t N = 270;

void findLargestBalanced(std::bitset<N>& a, size_t& p1s, size_t& p2s)
{
    // Step 1
    size_t p1 = 0;
    size_t p2 = N;
    int d = 2 * a.count() - N;
    bool flip = false;

    if (d == 0) {
        p1s = 0;
        p2s = N;
        return;
    }

    if (d < 0) {
        flip = true;
        d = -d;
        a.flip();
    }

    // Step 2
    bool next = true;
    while (d > 0) {
        if (p2 < N) {
            next = a[p2];
        }

        --d;
        --p2;

        if (a[p2] == false) {
            if (p2+1 < N) {
                a[p2+1] = false;
            }

            int dd = 2;
            while (dd > 0) {
                dd += (a[--p2]? -1: 1);
            }

            a[p2+1] = next;
            a[p2] = false;
        }
    }

    // Step 3
    p2s = p2;
    p1s = p1;

    do {
        // Step 4
        if (a[p2] == false) {
            a[p2++] = true;
            bool nextToRestore = a[p2];
            a[p2++] = true;

            int dd = 2;
            while (dd > 0 && p2 < N) {
                dd += (a[p2++]? 1: -1);
            }

            if (dd == 0) {
                a[--p2] = nextToRestore;
            }
        }
        else {
            ++p2;
        }

        // Step 5
        if (a[p1++] == false) {
            int dd = 2;
            while (dd > 0) {
                dd += (a[p1++]? -1: 1);
            }
        }

        // Step 6
        if (p2 - p1 > p2s - p1s) {
            p2s = p2;
            p1s = p1;
        }
    } while (p2 < N);

    if (flip) {
        a.flip();
    }
}

Ответ 6

Суммировать все элементы массива, затем diff = (array.length - sum) будет разница в числе 0s и 1s.

  • Если diff равен array.length/2, то максимальный subarray = array.
  • Если diff меньше, чем array.length/2, то больше 1s, чем 0s.
  • Если diff больше, чем array.length/2, то больше 0s, чем 1s.

Для случаев 2 и 3, инициализируйте два указателя, начало и конец, указывающие на начало и конец массива. Если у нас больше 1s, тогда переместите указатели внутрь (start ++ или end--) в зависимости от того, будет ли array [start] = 1 или array [end] = 1, и обновить сумму соответственно. На каждом шаге проверьте, если sum = (end-start)/2. Если это условие истинно, тогда начало и конец представляют границы вашего максимального подмассива.

Здесь мы заканчиваем выполнение двух проходов массива, один раз для вычисления суммы и один раз, который перемещает указатели внутрь. И мы используем постоянное пространство, так как нам просто нужно хранить суммы и два значения индекса.

Если кто-то хочет сбить какой-то псевдокод, вы более чем рады:)

Ответ 7

Здесь решение ActionScript, похожее на это, было масштабирование O (n). Хотя это может быть больше похоже на O (n log n). Он определенно использует только O (1) память.

Предупреждение Я не проверял, насколько это полно. Я мог бы пропустить некоторые случаи.

protected function findLongest(array:Array, start:int = 0, end:int = -1):int {
    if (end < start) {
        end = array.length-1;
    }

    var startDiff:int = 0;
    var endDiff:int = 0;
    var diff:int = 0;
    var length:int = end-start;
    for (var i:int = 0; i <= length; i++) {
        if (array[i+start] == '1') {
            startDiff++;
        } else {
            startDiff--;
        }

        if (array[end-i] == '1') {
            endDiff++;
        } else {
            endDiff--;
        }

        //We can stop when there no chance of equalizing anymore.
        if (Math.abs(startDiff) > length - i) {
            diff = endDiff;
            start = end - i;
            break;
        } else if (Math.abs(endDiff) > length - i) {
            diff = startDiff;
            end = i+start;
            break;
        }
    }

    var bit:String = diff > 0 ? '1': '0';
    var diffAdjustment:int = diff > 0 ? -1: 1;

    //Strip off the bad vars off the ends.
    while (diff != 0 && array[start] == bit) {
        start++;
        diff += diffAdjustment;
    }

    while(diff != 0 && array[end] == bit) {
        end--;
        diff += diffAdjustment;
    }

    //If we have equalized end. Otherwise recurse within the sub-array.
    if (diff == 0)
        return end-start+1;
    else
        return findLongest(array, start, end);      

}

Ответ 8

Я бы сказал, что невозможно, чтобы алгоритм с O (1) существовал следующим образом. Предположим, вы повторяете один раз за каждый бит. Для этого требуется счетчик, которому требуется пространство O (log n). Возможно, можно утверждать, что сама n является частью экземпляра проблемы, тогда у вас есть длина ввода для двоичной строки длины k: k + 2-log k. Независимо от того, как вы их просматриваете, вам нужна дополнительная переменная, если вам нужен индекс в этот массив, который уже делает его не O (1).

Обычно у вас нет этой проблемы, потому что у вас есть проблема размера n, вход n номеров журнала размера k, который добавляет до nlog k. Здесь переменная длины log k является просто O (1). Но здесь наш log k равен 1. Таким образом, мы можем ввести только переменную help, которая имеет постоянную длину (и я имею в виду, что она действительно постоянна, она должна быть ограничена независимо от того, насколько велика n).

Здесь одна проблема заключается в том, что описание проблемы становится видимым. В компьютерной теории вы должны быть очень осторожны в своей кодировке. Например. вы можете сделать NP проблемы полиномиальными, если вы переключитесь на унарное кодирование (потому что тогда размер ввода экспоненциально больше, чем в n-арной (n > 1) кодировке.

Что касается n, вход имеет только размер 2-log n, нужно быть осторожным. Когда вы говорите в этом случае O (n) - это действительно алгоритм, который является O (2 ^ n) (об этом нет необходимости обсуждать, потому что можно утверждать, является ли само n частью описания или нет).

Ответ 9

У меня этот алгоритм работает в O (n) времени и O (1) пространстве.

Он использует простой трюк "сжимать-затем-расширять". Комментарии в кодах.

public static void longestSubArrayWithSameZerosAndOnes() {
    // You are given an array of 1 and 0 only.
    // Find the longest subarray which contains equal number of 1 and 0's
    int[] A = new int[] {1, 0, 1, 1, 1, 0, 0,0,1};
    int num0 = 0, num1 = 0;

    // First, calculate how many 0s and 1s in the array
    for(int i = 0; i < A.length; i++) {
        if(A[i] == 0) {
            num0++;
        }
        else {
            num1++;
        }
    }
    if(num0 == 0 || num1 == 0) {
        System.out.println("The length of the sub-array is 0");
        return;
    }

    // Second, check the array to find a continuous "block" that has
    // the same number of 0s and 1s, starting from the HEAD and the
    // TAIL of the array, and moving the 2 "pointer" (HEAD and TAIL)
    // towards the CENTER of the array
    int start = 0, end = A.length - 1;
    while(num0 != num1 && start < end) {
        if(num1 > num0) {
            if(A[start] == 1) {
                num1--; start++;
            }
            else if(A[end] == 1) {
                num1--; end--;
            }
            else {
                num0--; start++;
                num0--; end--;
            }
        }
        else if(num1 < num0) {
            if(A[start] == 0) {
                num0--; start++;
            }
            else if(A[end] == 0) {
                num0--; end--;
            }
            else {
                num1--; start++;
                num1--; end--;
            }
        }
    }
    if(num0 == 0 || num1 == 0) {
        start = end;
        end++;
    }

    // Third, expand the continuous "block" just found at step #2 by
    // moving "HEAD" to head of the array and "TAIL" to the end of
    // the array, while still keeping the "block" balanced(containing
    // the same number of 0s and 1s
    while(0 < start && end < A.length - 1) {
        if(A[start - 1] == 0 && A[end + 1] == 0 || A[start - 1] == 1 && A[end + 1] == 1) {
            break;
        }
        start--;
        end++;
    }
    System.out.println("The length of the sub-array is " + (end - start + 1) + ", starting from #" + start + " to #" + end);

}

Ответ 10

линейное время, постоянное пространство. Дайте мне знать, если есть ошибка, которую я пропустил.
протестирован в python3.

def longestBalancedSubarray(A):
    lo,hi = 0,len(A)-1
    ones = sum(A);zeros = len(A) - ones
    while lo < hi:
        if ones == zeros: break
        else:
            if ones > zeros:
                if A[lo] == 1: lo+=1; ones-=1
                elif A[hi] == 1: hi+=1; ones-=1
                else: lo+=1; zeros -=1
            else:
                if A[lo] == 0: lo+=1; zeros-=1
                elif A[hi] == 0: hi+=1; zeros-=1
                else: lo+=1; ones -=1
    return(A[lo:hi+1])