find largest submatrix algorithm

前端 未结 2 1031
暖寄归人
暖寄归人 2021-02-09 13:24

I have an N*N matrix (N=2 to 10000) of numbers that may range from 0 to 1000. How can I find the largest (rectangular) submatrix that consists of the same number?

Exampl

2条回答
  •  温柔的废话
    2021-02-09 14:09

    This is an order Rows*Columns Solution

    It works by

    • starting at the bottom of the array, and determining how many items below each number match it in a column. This is done in O(MN) time (very trivially)
    • Then it goes top to bottom & left to right and sees if any given number matches the number to the left. If so, it keeps track of how the heights relate to each other to track the possible rectangle shapes

    Here is a working python implementation. Apologies since I'm not sure how to get the syntax highlighting working

    # this program finds the largest area in an array where all the elements have the same value
    # It solves in O(rows * columns) time  using  O(rows*columns) space using dynamic programming
    
    
    
    
    def max_area_subarray(array):
    
        rows = len(array)
        if (rows == 0):
            return [[]]
        columns = len(array[0])
    
    
        # initialize a blank new array
        # this will hold max elements of the same value in a column
        new_array = []
        for i in range(0,rows-1):
            new_array.append([0] * columns)
    
        # start with the bottom row, these all of 1 element of the same type 
        # below them, including themselves
        new_array.append([1] * columns)
    
        # go from the second to bottom row up, finding how many contiguous
        # elements of the same type there are
        for i in range(rows-2,-1,-1):
            for j in range(columns-1,-1,-1):
                if ( array[i][j] == array[i+1][j]):
                    new_array[i][j] = new_array[i+1][j]+1
                else:
                    new_array[i][j] = 1
    
    
        # go left to right and match up the max areas
        max_area = 0
        top = 0
        bottom = 0
        left = 0
        right = 0
        for i in range(0,rows):
            running_height =[[0,0,0]]
            for j in range(0,columns):
    
                matched = False
                if (j > 0):  # if this isn't the leftmost column
                    if (array[i][j] == array[i][j-1]):
                        # this matches the array to the left
                        # keep track of if this is a longer column, shorter column, or same as 
                        # the one on the left
                        matched = True
    
                        while( new_array[i][j] < running_height[-1][0]):
                            # this is less than the one on the left, pop that running 
                            # height from the list, and add it's columns to the smaller
                            # running height below it
                            if (running_height[-1][1] > max_area):
                                max_area = running_height[-1][1]
                                top = i
                                right = j-1
                                bottom = i + running_height[-1][0]-1
                                left = j - running_height[-1][2]
    
                            previous_column = running_height.pop()
                            num_columns = previous_column[2]
    
                            if (len(running_height) > 0):
                                running_height[-1][1] += running_height[-1][0] * (num_columns)
                                running_height[-1][2] += num_columns
    
                            else:
                                # for instance, if we have heights 2,2,1
                                # this will trigger on the 1 after we pop the 2 out, and save the current
                                # height of 1,  the running area of 3, and running columsn of 3
                                running_height.append([new_array[i][j],new_array[i][j]*(num_columns),num_columns])
    
    
                        if (new_array[i][j] > running_height[-1][0]):
                            # longer then the one on the left
                            # append this height and area
                            running_height.append([new_array[i][j],new_array[i][j],1])
                        elif (new_array[i][j] == running_height[-1][0]):   
                            # same as the one on the left, add this area to the one on the left
                            running_height[-1][1] += new_array[i][j]
                            running_height[-1][2] += 1
    
    
    
                if (matched == False or j == columns -1):
                    while(running_height):
                        # unwind the maximums & see if this is the new max area
                        if (running_height[-1][1] > max_area):
                            max_area = running_height[-1][1]
                            top = i
                            right = j
                            bottom = i + running_height[-1][0]-1
                            left = j - running_height[-1][2]+1
    
                            # this wasn't a match, so move everything one bay to the left
                            if (matched== False):
                                right = right-1
                                left = left-1
    
    
                        previous_column = running_height.pop()
                        num_columns = previous_column[2]
                        if (len(running_height) > 0):
                            running_height[-1][1] += running_height[-1][0] * num_columns
                            running_height[-1][2] += num_columns
    
                if (matched == False):
                    # this is either the left column, or we don't match to the column to the left, so reset
                    running_height = [[new_array[i][j],new_array[i][j],1]]
                    if (running_height[-1][1] > max_area):
                        max_area = running_height[-1][1]
                        top = i
                        right = j
                        bottom = i + running_height[-1][0]-1
                        left = j - running_height[-1][2]+1
    
    
        max_array = []
        for i in range(top,bottom+1):
            max_array.append(array[i][left:right+1])
    
    
        return max_array
    
    
    
    numbers = [[6,4,1,9],[5,2,2,7],[2,2,2,1],[2,3,1,5]]
    
    for row in numbers:
        print row
    
    print
    print
    
    max_array =  max_area_subarray(numbers)    
    
    
    max_area = len(max_array) * len(max_array[0])
    print 'max area is ',max_area
    print 
    for row in max_array:
        print row
    

提交回复
热议问题