Skip to content

Commit 96f413a

Browse files
author
Albert Hu
authored
Merge pull request #9 from alberthu16/day14
Day 14: Find 2D pattern in matrix
2 parents 9dedd6b + c654f9b commit 96f413a

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed

day14/2d-pattern-in-matrix.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
########
2+
# Code #
3+
########
4+
5+
# pattern and matrix are both 2D arrays
6+
# return true as soon as the pattern is found for a first time
7+
def patternInMatrix(pattern, matrix):
8+
rows, cols = len(matrix), len(matrix[0])
9+
patternRows, patternCols = len(pattern), len(pattern[0])
10+
for row in xrange(rows-patternRows):
11+
for col in xrange(cols-patternCols):
12+
found = False
13+
for pRow in xrange(patternRows):
14+
for pCol in xrange(patternCols):
15+
if pattern[pRow][pCol] != matrix[row+pRow][col+pCol]:
16+
found = False
17+
break
18+
else:
19+
found = True
20+
if found:
21+
return True
22+
return False
23+
24+
# return a list of all top-left starting indices
25+
def indicesOfPatternInMatrix(pattern, matrix):
26+
rows, cols = len(matrix), len(matrix[0])
27+
patternRows, patternCols = len(pattern), len(pattern[0])
28+
29+
startingSpots = []
30+
for row in xrange(rows-patternRows+1):
31+
for col in xrange(cols-patternCols+1):
32+
found = False
33+
for pRow in xrange(patternRows):
34+
for pCol in xrange(patternCols):
35+
if pattern[pRow][pCol] != matrix[row+pRow][col+pCol]:
36+
found = False
37+
break
38+
else:
39+
found = True
40+
if found:
41+
startingSpots.append((row, col))
42+
return startingSpots
43+
44+
#########
45+
# Tests #
46+
#########
47+
48+
def testPatterInMatrix():
49+
assert patternInMatrix([[1]], [[0,0,0],[0,1,0],[0,0,0]])
50+
assert not patternInMatrix([[2]], [[0,0,0],[0,1,0],[0,0,0]])
51+
52+
def testIndicesOfPatternInMatrix():
53+
p0 = [[1]]
54+
55+
m0 = [[0,0,0],
56+
[0,1,0],
57+
[0,0,0]]
58+
assert indicesOfPatternInMatrix(p0, m0) == [(1, 1)]
59+
60+
p1 = [[2]]
61+
62+
m1 = [[0,0,0],
63+
[0,1,0],
64+
[0,0,0]]
65+
assert indicesOfPatternInMatrix(p1, m1) == []
66+
67+
p2 = [[0,1,0]]
68+
69+
m2 = [[0,0,0],
70+
[0,1,0],
71+
[0,0,0]]
72+
assert indicesOfPatternInMatrix(p2, m2) == [(1, 0)]
73+
74+
p3 = [[0,0,0],
75+
[0,1,0],
76+
[0,0,0]]
77+
78+
m3 = [[0,0,0],
79+
[0,1,0],
80+
[0,0,0]]
81+
assert indicesOfPatternInMatrix(p3, m3) == [(0,0)]
82+
83+
p4 = [[0]]
84+
85+
m4 = [[0,0,0],
86+
[0,1,0],
87+
[0,0,0]]
88+
assert indicesOfPatternInMatrix(p4, m4) == [(0,0),(0,1),(0,2),
89+
(1,0),(1,2),
90+
(2,0),(2,1),(2,2)]
91+
92+
p4 = [[0, 0]]
93+
94+
m4 = [[0,0,0],
95+
[0,1,0],
96+
[0,0,0]]
97+
assert indicesOfPatternInMatrix(p4, m4) == [(0,0),(0,1),
98+
(2,0),(2,1)]
99+
100+
def main():
101+
testPatterInMatrix()
102+
testIndicesOfPatternInMatrix()
103+
104+
if __name__ == "__main__":
105+
main()

day14/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
Question of the day: https://www.careercup.com/question?id=5642834636963840
2+
3+
Given a 2D array of digits, try to find the occurence of a given 2D
4+
pattern of digits. For example, consider the following 2D matrix:
5+
6+
```python
7+
7283455864
8+
6731158619
9+
8988242643
10+
3839505324
11+
9509505813
12+
3843845384
13+
6473530293
14+
7053106601
15+
0834282956
16+
4607924137
17+
```
18+
19+
Assume we need to look for the following 2D pattern:
20+
21+
```python
22+
950
23+
384
24+
353
25+
```
26+
27+
Return a list of the top left indices of an occurence of the pattern in
28+
the 2D matrix. If there are multiple occurences, return all of the
29+
possible indices.
30+
31+
## Ideas
32+
33+
I'm going to make some assumptions to get myself started. I can explore
34+
these assumptions more in the follow-up section later on.
35+
36+
1. We don't care about finding rotations of the 2D pattern inside the matrix.
37+
2. We don't care about finding parts of the 2D patten inside the matrix
38+
3. The dimensions of the 2D pattern are always less than or equal to the respective dimensions of the matrix.
39+
40+
A brute force solution would be to iterate through all possible locations
41+
in the matrix and check if the 2D pattern matches starting at that
42+
location. Let's define `width` and `height` to be the width and height
43+
of the matrix. It would take `O(width*height)` time just to go through
44+
all the possible starting locations. Let's define `pWidth` and `pHeight`
45+
to be the width and height of the 2D pattern. So then if the pattern
46+
matches, there would be an additional `O(pWidth*pHeight)` per location
47+
with a possible start point, which excludes parts of the matrix. In the
48+
worst case, the runtime to this brute force solution is
49+
`O((width - pWidth + 1) * (height - pHeight + 1) * pWidth * pHeight)`. I do a
50+
subtraction which is key, because in a matrix like this:
51+
52+
```python
53+
11111
54+
11111
55+
11111
56+
11111
57+
11111
58+
```
59+
60+
with a 2D pattern like this:
61+
62+
```python
63+
1111
64+
1111
65+
1111
66+
1111
67+
```
68+
69+
there are only 4 possible starting locations: `(0, 0), (0, 1), (1,0), (1,1)`.
70+
71+
I don't think it's possible to do this problem and faster. So it's not
72+
a very complex problem, now I just have to write the code.
73+
74+
## Code
75+
76+
[Python](./2D-pattern-in-matrix.py)
77+
78+
## Follow up
79+
80+
@jjwon0 made a great [observation](https://github.com/alberthu16/100-day-coding-challenge/pull/9#discussion_r113083672)
81+
that the Knuth-Morris-Pratt linear time string matching algorithm may
82+
be applied to 2D pattern matching to make this more efficient.
83+
84+
Read the [wikipedia](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm)
85+
article on the algorithm, and then read this guy's [blog post](http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/)
86+
on what's going on with the preprocessing step.

0 commit comments

Comments
 (0)