5
\$\begingroup\$

I’ve written an answer for The Grid Search challenge on HackerRank. Please let me know of any improvements that can be made to my implementation (specifically if there's any way to make the algorithm more efficient. I'd like to make this algorithm optimal).

P.S: My implementation is a brute force algorithm, and I reckon that there's a much better way of solving this problem. But, as the grid is unsorted, I'm not exactly sure what that better method would be.

Problem statement (as written on HackerRank):

Given a 2D array of digits, try to find the location of a given 2D pattern of digits.

Answer:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Solution {
    public static void main(String[] args) throws IOException {
        Solution solution = new Solution();
        try(BufferedReader input = new BufferedReader(new InputStreamReader(System.in))){
            int numTestCases = Integer.parseInt(input.readLine());
            assert numTestCases >= 1 && numTestCases <= 5;
            int[][] grid;
            int[][] pattern;

            for(int t = 0; t < numTestCases; t++) {
                grid = solution.buildArray(input);
                pattern = solution.buildArray(input);
                // Gotta STDOUT in HackerRank for problem to be graded.
                System.out.println(solution.findPattern(grid, pattern));
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    /**
     * Determines whether pattern exists in grid.
     *
     * @param grid       2D array of digits
     * @param pattern    the 2D sequence of digits to be found in grid
     * @return "YES" if grid contains pattern. "NO" if not.
     */
    public String findPattern(int[][] grid, int[][] pattern){
        // R, C, r, & c are the same letters used in the problem
        for(int R = 0; R < grid.length - pattern.length + 1; R++){
            for(int C = 0; C < grid[0].length - pattern[0].length + 1; C++){
                outerLoop:
                for(int r = 0; r < pattern.length; r++){
                    for(int c = 0; c < pattern[0].length; c++){
                        if(grid[R + r][C + c] != pattern[r][c]){
                            break outerLoop;
                        }
                    }
                    if(r == pattern.length - 1){
                        return "YES";
                    }
                }
            }
        }
        return "NO";
    }

    /**
     * Builds and returns an array built according to data received from STDIN.
     *
     * @param input    STDIN stream
     * @return a 2D primitive array containing 0-9 digits
     * @throws IOException
     */
    public int[][] buildArray(BufferedReader input) throws IOException {
        String[] sizeParameters = input.readLine().split(" ");
        int rows = Integer.parseInt(sizeParameters[0]);
        int columns = Integer.parseInt(sizeParameters[1]);
        int[][] array = new int[rows][columns];

        for(int i = 0; i < rows; i++){
            String rowOfNumbers = input.readLine();
            for(int j = 0; j < columns; j++){
                array[i][j] = Character.getNumericValue(rowOfNumbers.charAt(j));
            }
        }

        return array;
    }

}
\$\endgroup\$
4
  • \$\begingroup\$ What is your primary goal for this code review? Do you want a more effective solution, improved code quality, less bugs/robust code? If you'd use Java 8 features, there's a lot to gain through Stream API. \$\endgroup\$
    – SME_Dev
    Commented Apr 9, 2015 at 15:03
  • \$\begingroup\$ Ideally a more effective solution if possible. \$\endgroup\$ Commented Apr 9, 2015 at 15:05
  • \$\begingroup\$ How is the compition evaluated? Is the file processing / generation of a 2dim. array already part of the task? \$\endgroup\$
    – SME_Dev
    Commented Apr 9, 2015 at 15:15
  • \$\begingroup\$ I'm not sure what you mean tbh. A number of test cases are provided, which are then read using BufferedReader. Have a look on the Input Format part of the problem statement for details: hackerrank.com/challenges/the-grid-search \$\endgroup\$ Commented Apr 9, 2015 at 15:40

2 Answers 2

3
\$\begingroup\$

Code organization

This is usually a cause for concern:

Solution solution = new Solution();

Programming contests do not generally require much object design. And in this particular case, instance methods just obfuscate reading. Make them public static. And if it gets so big that you need to divvy it up, do it properly, then.

This is usually a cause for concern, too:

    }catch(IOException e){
        e.printStackTrace();
    }

Just throw the exception, if you cannot do anything with it. And some automated judges show you the STDERR but not STDOUT, so you may be wasting your chance to see whether something went wrong or you returned wrong answer, and what went wrong. (Just knowing if your program has thrown an exception is good. If the automated judge does not support exception but supports process exit codes, you do a System.exit if an exception thrown.)

Performance

Some contests test your code with just small and large data sets, which means you need to find and algorithm with good average complexity. Some contest, such as ACM, tests the code also with the known worst case scenarios of the best known algorithms that could be used to solve the problem. In that case you need to find good worst case complexity.

Some contests have inputs to weed out naive implementations as your current one. One such case would be 1000X1000 grid of 1s, and 500X500 pattern of 1s with a 2 in the bottom right corner. Against such a case you can try an algorithm like :

  • Scan the pattern, find the row with most diversity (e.g. with the most elements which have a different value to its right)
  • for each row in the grid do a KMP substring search.
  • for each match compare the rest of the pattern in the grid, using the row and column offset of the match.

Which, I believe, will have a complexity : O(R(C+c) + m(rc)), where m is the number of the matches for the pattern row used; which can still be bad for large m. In that case you can do a search for a pattern column in parallel (real or simulated).

\$\endgroup\$
-1
\$\begingroup\$

Before starting with

for (int r = 0; r < pattern.length; r++) {

You can add one more check:

if (grid[R + r][C + c] == pattern[r][c]) {
    //only then start comparing
}

By doing this check, you can avoid unnecessary iterations of the inner for loops(the ones which compare the pattern)

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Hi, welcome to Code Review. I have edited your post to format properly the code. Please refer to How to Answer and the editing help. Your answer doesn't explain why the OP should add this check. It could also review the other part of the code to make your answer better. \$\endgroup\$
    – Tunaki
    Commented Apr 7, 2016 at 10:22
  • \$\begingroup\$ @Tunaki - thanks for the feedback... Will keep these things in mind :) \$\endgroup\$ Commented Apr 8, 2016 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.