Попытка выяснить самый длинный алгоритм пути python

Я пытаюсь создать скрипт Python, который дает мне самый длинный повторяющийся символ в заданной матрице (по горизонтали и вертикали).

Пример:

У меня есть эта матрица:

afaaf
rbaca
rlaff

При подаче этой матрицы на вход должно получиться: a 3

Вы можете видеть, что 3-й столбец матрицы заполнен буквами «а», а также это самый повторяющийся символ в матрице.

Что у меня есть:

#!/bin/python2.7

#Longest string in matrix

#Given a matrix filled with letters. Find the longest string, containing only the same letter, which can be obtained by starting
#with any position and then moving horizontally and vertically (each cell can be visited no more than 1 time).


# Settings here
# -------------
string_matrix = """
afaaf
rbaca
rlaff
"""

pos = (0,0)
# -------------

import pdb
import time
import collections
from collections import defaultdict
import re


rows = 0
columns = 0
matrix = []
matrix2 = []
counter = 0
res_l = []
i = 0
c = ''

# if matrix2 is full of 1's, stop
def stop():
    for i in range(0, rows):
        for j in range(0, columns):
            if matrix2[i][j] == 0:
                return False
    return True

# checks the points, and returns the most repeated char and length
def check_points(points1, points2):
    r = []

    r.append(-1)
    r.append('')

    # create strings from matrix
    s1 = ''
    s2 = ''


    for point in points1:
        s1 += matrix[point[0]][point[1]]

    for point in points2:
        s2 += matrix[point[0]][point[1]]

    rr = {}

    for c in s1:
        rr[c] = 0

    for c in s2:
        rr[c] = 0

    for i in range(0, len(s1)):
        k = 1
        for j in range(i+1, len(s1)):
            if s1[i] == s1[j]:
                k += 1
            else:
                break
            if k > rr[s1[i]]:
                rr[s1[i]] = k


    for i in range(0, len(s2)):
        k = 1
        for j in range(i+1, len(s2)):
            if s2[i] == s2[j]:
                k += 1
            else:
                break
            if k > rr[s2[i]]:
                rr[s2[i]] = k


    m = -1
    c = ''
    for key,value in rr.iteritems():
        if value > m:
            m = value
            c = key

    return m, c


# Depth-first search, recursive
def search(pos):
    global res_l
    global matrix2
    global c

    counter = 0

    x = pos[0]
    y = pos[1]

    c = matrix[x][y]

    # base clause
    # when matrix2 is all checked
    if stop():
        return counter, c



    points1 = []
    points2 = []
    allpoints = []

    for i in range(0, columns):
            if matrix2[x][i] != 1:
                points1.append([x, i])
                allpoints.append([x, i])


    for i in range(0, rows):
            if matrix2[i][x] != 1:
                points2.append([i, x])
                allpoints.append([i, x])





    r = check_points(points1, points2)


    if r[0] > counter:
        counter = r[0]
        c = r[1]

    matrix2[x][y] = 1


    for point in allpoints:
        rr = search(point)
        if rr[0] > counter:
            counter = int(rr[0])
            c = rr[1]
            #print 'c: ' + str(c) + ' - k: ' + str(counter)



    return counter, c

def main():
    # create the matrix from string
    string_matrix_l = string_matrix.strip()
    splited = string_matrix_l.split('\n')


    global rows
    global columns
    global matrix
    global matrix2

    rows = len(splited)
    columns = len(splited[1])



    # initialize matrixes with 0
    matrix = [[0 for x in range(columns)] for x in range(rows)]
    matrix2 = [[0 for x in range(columns)] for x in range(rows)]


    # string to matrix
    i = 0
    for s in splited:
        s = s.strip()
        if s == '':
            continue

        j = 0

        for c in s:
            try:## Heading ##
                matrix[i][j] = c
                #print 'ok: ' + str(i) + ' ' + str(j) + ' ' +  c
            except:
                print 'fail: index out of range matrix[' + str(i) + '][' + str(j)+'] ' + c
            j = j + 1

        i = i + 1


    # print some info
    print 'Given matrix: ' + str(matrix) + '\n'
    print 'Start position: ' + str(pos)
    print 'Start character: ' + str(matrix[pos[0]][pos[1]])

    # get the result
    res = search(pos)
    print '-------------------------------------'
    print '\nChar: ' + str(res[1]) + '\nLength: ' + str(res[0])


if __name__ == "__main__":
    main()

Это мой исходный код. Пример, приведенный выше, также используется в исходном коде. Полученный результат: r 2, что неверно... снова должно быть a 3.

Имеет 4 функции: основная, поиск, остановка и контрольные_точки.

  • main инициализирует вещи,
  • search — это моя рекурсивная функция, которая принимает один параметр (начальную точку) и должна рекурсивно проверять самую длинную строку. У меня есть другая матрица той же длины, что и исходная, которая состоит только из 1 и 0. 1 означает, что позиция была посещена, 0 - нет. Функция поиска устанавливает 1 в правильном положении после того, как определенная позиция была обработана функцией поиска.
  • stop проверяет, заполнена ли матрица2 единицами, в этом случае вся матрица была проанализирована
  • check_points принимает 2 параметра, 2 списка точек и возвращает наиболее повторяющийся символ и его длину для этих точек.

Что не работает:

Большую часть времени в результате я получаю неправильный символ, даже если иногда счет может быть правильным. Иногда работает по горизонтали, иногда нет. Я уверен, что делаю что-то не так, но... уже более 1 недели, как я пытаюсь понять, как это сделать. Задал еще один вопрос здесь, в stackoverflow, немного продвинулся, но ... все еще застрял.

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


person user1812076    schedule 08.04.2015    source источник
comment
Описание, кажется, позволяет нам поворачиваться, следуя цепочке букв. Это разрешено? Если да, то есть цепочка из 4-х.   -  person user2357112 supports Monica    schedule 08.04.2015
comment
нет, такой комплекс мне не нужен. Просто по вертикали и по горизонтали, он не может двигаться в обе стороны одновременно.   -  person user1812076    schedule 08.04.2015
comment
Хм, ваш подход далек от правильного, поэтому я предлагаю вам попробовать свой код на примере разумного размера, чтобы увидеть, что пошло не так и чего следует ожидать. Для этой проблемы простейшим подходом является перебор всех строк и столбцов, который требует O(n) на строку/столбец, что совсем неплохо.   -  person Pham Trung    schedule 08.04.2015
comment
Я пытаюсь сделать это уже больше недели ... Я испробовал все возможные подходы, о которых я знал.   -  person user1812076    schedule 08.04.2015
comment
Несколько подсказок: вам нужно позаботиться о проблеме направления, поэтому ваш поиск теперь идет в любом направлении, но это неправильно. Во-вторых, вы должны ограничить направление движения вправо и только вниз. В-третьих, у вас может быть цикл for для проверки каждой позиции unchecked, не пытайтесь упаковать их в один рекурсивный вызов в main, это усложнит вам жизнь. И если вы все еще не можете решить ее, вернитесь к простой версии этой проблемы, если у вас есть однострочная строка, как вы можете решить эту проблему.   -  person Pham Trung    schedule 08.04.2015
comment
ну, как и в функции check_points, я бы использовал словарь, добавлял в него (ключ) каждый символ из строки и в зависимости от того, сколько раз он встречается, увеличивал его значение.   -  person user1812076    schedule 08.04.2015
comment
Даже это тоже неправильно, так вы не проверяете самый длинный повторяющийся путь. Что произойдет, если строка будет ababa ? результат один, но ваш подход дает 3   -  person Pham Trung    schedule 08.04.2015
comment
как мне тогда это проверить?   -  person user1812076    schedule 08.04.2015
comment
Если вы не можете решить эту простую версию, как вы можете решить эту проблему? Подумай больше, мой друг, зачем тебе нужно решать эту проблему? Задание или вы хотите быть лучше?   -  person Pham Trung    schedule 08.04.2015
comment
@PhamTrung, если бы вы проверили функцию check_string, вы бы знали, как я это сделал. Забыл упомянуть, что счетчик сбросится для всех ключей, если char (ключ) не совпадает с предыдущим. И да, это задание, но что в этом плохого? Я не пытаюсь обмануть, просто выяснить, что делать, чтобы решить проблему.   -  person user1812076    schedule 08.04.2015
comment
Если вы можете сделать это с помощью одной строки, почему вы не можете просто сделать это со всеми отдельными строками и столбцами? Хм, я думаю, это то же самое, что вам предложил Кольмар. Нет, я не критикую вас, а просто хочу, чтобы вы сами придумали, как это сделать, вы очень близки на самом деле после всех наших усилий. Но хорошо,...   -  person Pham Trung    schedule 08.04.2015
comment
Да, у меня есть идея, как это сделать, но дело в том, что я забыл упомянуть, что мне тоже нужно, чтобы это было рекурсивно, я не знаю, действительно ли ответ Колмара рекурсивен, но это определенно ответ на мой вопрос.   -  person user1812076    schedule 08.04.2015


Ответы (2)


Вы можете использовать itertools.groupby, чтобы быстро найти количество повторений какого-либо символа, и izip_longest(*matrix), чтобы транспонировать матрицу (поменять местами ее строки и столбцы).

from itertools import groupby, izip_longest

matrix_string = """
afaaf
rbaca
rlaff
"""

def longest_repetition(row): 
    return max((sum(1 for item in group), letter) 
               for letter, group in groupby(row) 
               if letter is not None)

def main():
    matrix = [[letter for letter in row.strip()] 
              for row in matrix_string.strip().split('\n')]

    count, letter = max(
        max(longest_repetition(row) for row in matrix),
        max(longest_repetition(col) for col in izip_longest(*matrix))
    )

    print letter, count

if __name__ == '__main__':
    main()

Поскольку вы обновили требование, вот рекурсивная версия кода с некоторыми пояснениями. Если бы это не было заданием, и эта задача возникла в какой-то реальной жизненной проблеме, вам действительно следовало бы использовать первую версию.

matrix_string = """
afaaf
rbaca
rlaff
"""


def find_longest_repetition(matrix):
    rows = len(matrix)
    cols = len(matrix[0])

    # row, col - row and column of the current character.
    # direction - 'h' if we are searching for repetitions in horizontal direction (i.e., in a row).
    #             'v' if we are searching in vertical direction.
    # result - (count, letter) of the longest repetition we have seen by now.
    #          This order allows to compare results directly and use `max` to get the better one
    # current - (count, letter) of the repetition we have seen just before the current character.
    def recurse(row, col, direction, result, current=(0, None)):
        # Check if we need to start a new row, new column, 
        #   new direction, or finish the recursion.
        if direction == 'h':    # If we are moving horizontally
            if row >= rows:     # ...  and finished all rows
                return recurse( # restart from the (0, 0) position in vertical direction.
                    0, 0,
                    'v',
                    result
                )
            if col >= cols:     # ... and finished all columns in the current row
                return recurse( # start the next row.
                    row + 1, 0,
                    direction,
                    result
                )
        else:                   # If we are moving vertically
            if col >= cols:     # ... and finished all columns
                return result   # then we have analysed all possible repetitions. 
            if row >= rows:     # ... and finished all rows in the current column
                return recurse( # start the next column.
                    0, col + 1,
                    direction,
                    result
                )

        # Figure out where to go next in the current direction
        d_row, d_col = (0, 1) if direction == 'h' else (1, 0)

        # Try to add current character to the current repetition
        count, letter = current
        if matrix[row][col] == letter:
            updated_current = count + 1, letter
        else:
            updated_current = 1, matrix[row][col]

        # Go on with the next character in the current direction
        return recurse( 
            row + d_row, 
            col + d_col,
            direction,
            max(updated_current, result), # Update the result, if necessary
            updated_current
        )

    return recurse(0, 0, 'h', (0, None))


def main():
    matrix = [[letter for letter in row.strip()] 
              for row in matrix_string.strip().split('\n')]

    count, letter = find_longest_repetition(matrix)

    print letter, count


if __name__ == '__main__':
    main()
person Kolmar    schedule 08.04.2015
comment
вау, это работает! но кроме этого, не могли бы вы объяснить немного больше, что происходит? - person user1812076; 08.04.2015
comment
@user1812076 user1812076 Конечно, я сделаю это через несколько часов. (У меня уже назначена встреча) А пока вы можете поиграть с groupby и сами посмотреть, как это работает. - person Kolmar; 08.04.2015
comment
@ user1812076 Я добавил рекурсивную версию кода. - person Kolmar; 08.04.2015

Вы также можете попробовать коллекции .Counter(string).most_common(), чтобы получить наибольшее количество повторений символа.

from collections import Counter

string_matrix = """
afaaf
rbaca
rlaff
"""

def GetMostRepetitions(pos):
    mc = []

    for ii in range(pos[0],len(working_mat)):
        mc.extend(Counter(working_mat[ii]).most_common(1))  

        for jj in range(pos[1],len(working_mat[0])):
            column = []           

            for kk in range(ii,len(working_mat)):
                column.append(working_mat[kk][jj])

            mc.extend(Counter(column).most_common(1))          

    m = 0           
    for item in mc: 
        if item[1] > m:
            m = item[1]
            k = item[0]


    print(k, m)                 

working_mat = string_matrix.strip().split('\n')

for ii in range(len(working_mat)):
    for jj in range(len(working_mat[0])):
        pos = (ii,jj)
        GetMostRepetitions(pos)

Как сказал Kolmar, вы также можете использовать лучший способ транспонировать матрицу.

person Repiklis    schedule 08.04.2015
comment
спасибо за решение, это тоже работает! В любом случае я мог бы изменить его для рекурсивной работы с функцией? - person user1812076; 08.04.2015
comment
@user1812076 user1812076 Обновлено для работы с разными позициями. - person Repiklis; 08.04.2015
comment
спасибо, я вижу, что сейчас он использует функцию, но... я не думаю, что это рекурсивно, не так ли? Я действительно ценю ответ, и это действительно то, что я хотел! Но, если я не прошу слишком многого, мне это понадобится рекурсивно, и я думаю, что функция должна вызывать сама себя. - person user1812076; 08.04.2015
comment
@user1812076 user1812076 Нет, это не рекурсивная функция. Почему вы хотите сделать это таким образом? - person Repiklis; 08.04.2015
comment
Ну, это то, чего я пытаюсь добиться в основном, но забыл упомянуть в исходном вопросе. Как я уже сказал, это тоже круто, но если вы знаете ответ, а также объясните всего в нескольких строках, я был бы очень признателен. - person user1812076; 08.04.2015
comment
@user1812076 user1812076 Вы все еще не объясняете, зачем вам нужна рекурсивная функция. Я не смогу вам помочь, если вы не дадите более подробной информации. - person Repiklis; 08.04.2015
comment
Ну, причина в том, что я должен сделать это с рекурсией, это задание, но, чтобы вы не поняли неправильно, я не пытаюсь обмануть или скопировать решения, я пытаюсь решить проблему и также понять, что происходит. - person user1812076; 08.04.2015