Самая длинная подстрока без Y разделяй и властвуй - PullRequest
0 голосов
/ 07 ноября 2018

Прежде чем перейти к проблеме, я должен отметить, что я знаю, что есть гораздо более простые способы решить эту проблему без использования разделяй и властвуй; Однако смысл решения этой проблемы в рамках этого ограничения заключается в том, что я действительно хочу научиться решать проблемы с разделяй и властвуй. Я умею распознавать правильные решения, но реализация моей собственной стратегии D & C - это не тот навык, которым я сейчас обладаю.

Проблема заключается в следующем: по заданной строке найдите самую длинную подстроку, которая не содержит буквы «у». Например, longestNoY ("abydefyhi") должен возвращать "def".

Моим первым подходом к решению этой проблемы было определение базовых случаев. Если бы у нас была строка длиной 2, мы бы хотели вернуть компоненты, отличные от y (или пустую строку, если оба символа были 'y'). Если бы у нас была строка длины 1, мы бы ее вернули, если это не «у».

Итак, первая часть должна выглядеть так:

def longestNoY(string, start, end):
     #Conquer
     if start == end:
          if string == 'y': return ''
          return string
     if start + 1 == end:
          if string == "yy": return ''
          if string[0] == 'y': return string[1]
          return string[0]
     ....

Далее я знал, что мне нужно будет рекурсивно вызывать функцию для каждой половины родительской строки. Я также знал, что хочу, чтобы функция возвращала более длинное из двух дочерних элементов, за исключением того, что если сумма длин двух дочерних элементов равна длине родительского элемента, то функция должна возвращать родительский элемент, потому что не было 'y's у детей.

    #Divide and Partial Implementation of Rejoin
    ....
    middle = (start + end) // 2
    leftString = longestNoY(string, start, middle)
    rightString = longestNoY(string, middle, end)
    if len(leftString) + len(rightString) == len(string): return string
    ....

Часть, с которой у меня сейчас проблемы, лучше всего объяснить на примере:

0 1 2     3 4   5 6   7 8 
a b   y   d e | f y   h i
a b   y | d e | f y | h i
a b | y | d e | f y | h i 

Самая длинная подстрока в левой части - это либо "ab", либо "de", но мы знаем, что "de" смежна с "f", что делает "def" самой длинной. Я не знаю точно, как продолжать эту проблему. Пожалуйста, не дайте мне программу для решения этой проблемы.

Ответы [ 4 ]

0 голосов
/ 06 декабря 2018

Теперь, когда у меня действительно было время учиться, я решил вернуться к этой проблеме и нашел очень удобочитаемое решение.Вот оно:

def across(string, start, end, middle):
    startL = middle
    bestL = ''
    while(startL >= start and string[startL] != 'y'):
        bestL = string[startL] + bestL
        startL -= 1
    startR = middle + 1
    bestR = ''
    while(startR <= end and string[startR] != 'y'):
        bestR = bestR + string[startR]
        startR += 1
    return bestL + bestR

def longestNoY(string, start, end):
    if(start > end):
        return ''
    if(start == end):
        if(string[start] == 'y'):
            return ''
        return string[start]
    middle = (start + end) // 2
    leftString = longestNoY(string, start, middle)
    rightString = longestNoY(string, middle + 1, end)
    acrossString = across(string, start, end, middle)
    return max(leftString, rightString, acrossString, key=len)
0 голосов
/ 07 ноября 2018

Это может быть легко решено простым обходом строки.Но я знаю, что вы хотите научиться Divide Conquer.

Для меня это не очень хорошая проблема, чтобы решить с помощью Divide Conquer.

То, что @WillemVanOnsem предлагает для рекурсии, по сути, имеет тот же эффект, что и при линейном перемещении.

Но если вы хотите сделать это в стиле «разделяй и властвуй», вам нужно рассмотреть подстроку, которая пересекает среднюю точку, то есть начало <= i <= mid <j <= конец - но это было бы излишним. </p>

0 голосов
/ 08 ноября 2018

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

  • В тривиальных случаях (длина интервала равна 0) определить, является ли имеющийся у вас элемент действительным левым концом или правым интервалом
  • В нетривиальных случаях всегда делите пополам набор влево и вправо (нет проблем, если число элементов нечетное, просто поставьте середину где-нибудь), а также выполните разделение и завоевание для них
  • В нетривиальных случаях всегда учитывайте наилучший интервал, который дает вам подзадача слева и справа
  • В нетривиальных случаях убедитесь, что если интервал начинается слева и заканчивается справа, вы учитываете это
  • из таких интервалов, тот, который имеет большую длину, лучше

Это идеи, которые вам нужно использовать, чтобы осуществить разделение и победить, которые вы желаете. Удачного кодирования!

0 голосов
/ 07 ноября 2018

Это возможно . Но тогда вам каждый раз нужно возвращать четыре значения: самая длинная подпоследовательность, которая начинается в левом конце «среза» (это может быть ноль), самая длинная подпоследовательность «в середине», самая длинная подпоследовательность он заканчивается на правом конце «среза» (это также может быть ноль), и если строка является просто последовательностью не-Y символов (логическое значение). Фактически, четвертый элемент может быть получен путем проверки, равен ли один из элементов в первых трех элементам длине, но это, вероятно, легче реализовать.

Почему это важно? Потому что последовательность не y s может проходить «через» деление. Например:

abcde<b>Y</b>fghi   jkl<b>Y</b>mnopqr

здесь, если мы разбиваем его по середине (или любым другим способом, который не является «постоянным» и «покоем»).

Так что здесь у нас есть несколько случаев:

  1. пустая строка возвращает (0, 0, 0, True),
  2. непустая строка, отличная от Y, мы возвращаем (1, 1, 1, True);
  3. для одиночной строки Y мы возвращаем (0, 0, 0, False);
  4. рекурсивный случай, который делит строку на две части и затем применяет логику «слияния» к результатам.

«Логика слияния» довольно сложна, тем более что возможно, что оба «субликса» содержат только строки, не являющиеся Y. После нарезки мы получаем две тройки (a0, a1, a2, a3) и (b0, b1, b2, b3) и получаем три кортежа (c0, c1, c2, c3).

Если a3 = True и b3 = True, то, конечно, это означает, что текущий срез также не содержит Y. Таким образом, мы можем вывести это:

c3 = a3 and b3

, учитывая, что a3 имеет место, тогда он утверждает, что c0 = a0 + b0 с тех пор a0 не имеет Y, и, таким образом, левая "последовательность" равна всей длине подпоследовательности плюс левого подпоследовательность правой части. Если a3 не удерживает , c0 просто a0.

Учитывая, что выполняется b3, то оно содержит c2 = a2 + b2 по тем же рассуждениям, что и выше, если нет, то a2 = b2.

Теперь элемент в середине - это максимум три элемента:

  1. элемент посередине левого среза a1;
  2. элемент в середине правого среза b1; и
  3. сумма a2 и b0, поскольку возможны совпадения, а затем это сумма двух.

Таким образом, мы возвращаем максимум дерева.

Так в Python это выглядит так:

def longestNoY(string, start, end):
    if start == end:
        return (0, 0, 0, True)
    elif start+1 == end:
        if string[start] == 'y':
            return (0, 0, 0, False)
        else:
            return (1, 1, 1, True)
    else:
        mid = (start + end)//2
        a0, a1, a2, a3 = longestNoY(string, start, mid)
        b0, b1, b2, b3 = longestNoY(string, mid, end)
        c3 = a3 and b3
        c0 = a0 + a3 * b0
        c2 = b2 + b3 * a2
        c1 = max(a1, b1, a2 + b0)
        return (c0, c1, c2, c3)

Окончательный результат - максимум первых трех элементов в кортеже.

Для данной строки образца мы получаем:

             (1, 1, 1, True) a
             (1, 1, 1, True) b
         (2, 2, 2, True) ab
             (0, 0, 0, False) y
             (1, 1, 1, True) d
         (0, 1, 1, False) yd
     (2, 2, 1, False) abyd
             (1, 1, 1, True) e
             (1, 1, 1, True) f
         (2, 2, 2, True) ef
             (0, 0, 0, False) y
                 (1, 1, 1, True) h
                 (1, 1, 1, True) i
             (2, 2, 2, True) hi
         (0, 2, 2, False) yhi
     (2, 2, 2, False) efyhi
 (2, 3, 2, False) abydefyhi
(2, 3, 2, False)

но, как говорится, для меня это выглядит как ненужная сложная процедура для конструирования чего-либо, что с точки зрения сложности времени такое же, как обход, но обычно более дорого (вызовы функций, создание новых объектов и т. Д.). Тем более что линейный обход это просто:

def longestNoY(string):
    mx = 0
    cur = 0
    for c in string:
        if c == 'y':
            mx = max(mx, cur)
            cur = 0
        else:
            cur += 1
    return mx

Однако здесь есть преимущество в том, что описанный выше алгоритм может использоваться для распараллеливания . Если, например, строка огромна, то можно использовать вышеприведенное, так что каждое ядро ​​может считать это. Однако в этом случае, вероятно, выгодно использовать итеративный уровень на уровне «ядра» и использовать только вышеперечисленное для «распределения» работы и «сбора» результатов.

...