3044. Most Frequent Prime
Problem Description
You are given a m x n
matrix mat
containing single digits (0-9). Your task is to find numbers by traversing the matrix in straight lines and determine which prime number appears most frequently.
From each cell in the matrix, you can move in 8 possible directions:
- East (right)
- South-east (diagonal down-right)
- South (down)
- South-west (diagonal down-left)
- West (left)
- North-west (diagonal up-left)
- North (up)
- North-east (diagonal up-right)
When traversing in any direction, you form numbers by concatenating the digits you encounter. For example, if you traverse cells containing digits 1
, 9
, 1
in sequence, you create three numbers: 1
, 19
, and 191
.
Rules and requirements:
- You must move in a straight line (cannot change direction during a single traversal)
- Only count prime numbers that are greater than 10
- Count how many times each valid prime number appears across all possible traversals from all starting positions
- Return the prime number that appears most frequently
- If multiple prime numbers have the same highest frequency, return the largest one among them
- If no prime number greater than 10 exists, return
-1
For example, starting from a cell with digit 1
, if you move east and encounter 9
, you've created the number 19
. If 19
is prime and greater than 10, it gets counted. If you continue east and encounter 1
, you've now created 191
, which also gets evaluated and counted if it's prime.
Intuition
The key insight is that we need to systematically explore all possible number formations from every cell in the matrix. Since we can move in 8 directions and must continue in a straight line, this naturally suggests a traversal approach.
Think of each cell as a potential starting point. From any cell (i, j)
, we can imagine drawing straight lines in all 8 directions. As we move along each line, we're essentially building numbers digit by digit - like typing digits on a calculator where each new digit shifts the previous number left by one decimal place (multiply by 10) and adds the new digit.
The mathematical pattern for building numbers is straightforward: if we have a number v
and encounter a new digit d
, the new number becomes v * 10 + d
. For instance, if we have 19
and encounter 1
, we get 19 * 10 + 1 = 191
.
For the direction movement, we can use direction vectors. Each direction can be represented by a pair (a, b)
where a
is the row change (-1, 0, or 1) and b
is the column change (-1, 0, or 1). This gives us 9 combinations, but we exclude (0, 0)
since that means no movement.
To track frequencies, a hash table (Counter) is perfect - it naturally handles counting occurrences of each prime number we find. As we generate each number along a path, we check if it's prime and greater than 10, then increment its count.
The prime checking itself is a standard algorithm - we only need to check divisibility up to the square root of the number, making it efficient enough for this problem.
Finally, finding the most frequent prime is a simple scan through our frequency table, keeping track of the maximum frequency and updating our answer when we find a higher frequency or a larger prime with the same frequency.
Learn more about Math patterns.
Solution Approach
The solution uses a hash table combined with exhaustive enumeration to solve this problem efficiently.
Step 1: Initialize Data Structures
- Use a
Counter
(hash table) to track the frequency of each prime number encountered - Get matrix dimensions
m
andn
for boundary checking
Step 2: Enumerate All Starting Positions
Iterate through each cell (i, j)
in the matrix as a potential starting point:
for i in range(m):
for j in range(n):
Step 3: Explore All 8 Directions
For each starting cell, try all 8 directions using direction vectors (a, b)
:
for a in range(-1, 2):
for b in range(-1, 2):
if a == 0 and b == 0:
continue # Skip no movement
The pairs (a, b)
represent:
(-1, -1)
: North-west(-1, 0)
: North(-1, 1)
: North-east(0, -1)
: West(0, 1)
: East(1, -1)
: South-west(1, 0)
: South(1, 1)
: South-east
Step 4: Traverse and Build Numbers
Starting from position (i, j)
, move in direction (a, b)
:
- Initialize with the starting cell's digit:
v = mat[i][j]
- Move to the next cell:
x, y = i + a, j + b
- Continue while within bounds:
0 <= x < m and 0 <= y < n
- Build the number:
v = v * 10 + mat[x][y]
- Check if it's a prime greater than 10 and update the counter
- Move to the next cell in the same direction:
x, y = x + a, y + b
Step 5: Prime Checking
The helper function is_prime(x)
checks if a number is prime by testing divisibility from 2 to sqrt(x)
:
all(x % i != 0 for i in range(2, isqrt(x) + 1))
This is efficient since any composite number must have a factor less than or equal to its square root.
Step 6: Find the Most Frequent Prime After collecting all prime frequencies:
- Initialize
ans = -1
(default return value) andmx = 0
(maximum frequency) - Iterate through the counter items
(v, x)
wherev
is the prime andx
is its frequency - If frequency
x
is greater thanmx
, update bothmx
andans
- If frequency equals
mx
, keep the larger prime:ans = max(ans, v)
This approach ensures we find all possible prime numbers formed by straight-line traversals and correctly identify the most frequent one, with ties broken by choosing the largest value.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this 3x3 matrix:
mat = [[1, 9, 7], [3, 1, 3], [2, 3, 1]]
Step 1: Start from cell (0,0) with value 1
Let's explore the East direction (a=0, b=1):
- Start with v = 1 (not prime, not > 10)
- Move to (0,1): v = 1*10 + 9 = 19
- Check: 19 > 10? Yes. Is 19 prime? Yes (not divisible by 2,3,4)
- Add to counter: {19: 1}
- Move to (0,2): v = 19*10 + 7 = 197
- Check: 197 > 10? Yes. Is 197 prime? Yes
- Add to counter: {19: 1, 197: 1}
- Can't move further (out of bounds)
Now explore South-East direction (a=1, b=1) from (0,0):
- Start with v = 1
- Move to (1,1): v = 1*10 + 1 = 11
- Check: 11 > 10? Yes. Is 11 prime? Yes
- Add to counter: {19: 1, 197: 1, 11: 1}
- Move to (2,2): v = 11*10 + 1 = 111
- Check: 111 > 10? Yes. Is 111 prime? No (111 = 3 × 37)
- Not added to counter
Step 2: Start from cell (0,1) with value 9
Let's explore South direction (a=1, b=0):
- Start with v = 9
- Move to (1,1): v = 9*10 + 1 = 91
- Check: 91 > 10? Yes. Is 91 prime? No (91 = 7 × 13)
- Move to (2,1): v = 91*10 + 3 = 913
- Check: 913 > 10? Yes. Is 913 prime? No (913 = 11 × 83)
Step 3: Continue for all cells and directions
After checking all starting positions and directions, let's say we found:
- 19 appears 2 times (from different starting points/directions)
- 11 appears 3 times
- 197 appears 1 time
- 31 appears 2 times
- 13 appears 4 times
Step 4: Find the most frequent prime
Scanning through our counter:
- 13 has frequency 4 (highest)
- No other prime has frequency 4
Therefore, the answer is 13.
If we had a tie (e.g., both 13 and 31 appeared 4 times), we would return 31 as it's larger.
Solution Implementation
1class Solution:
2 def mostFrequentPrime(self, mat: List[List[int]]) -> int:
3 """
4 Find the most frequent prime number formed by traversing the matrix in any direction.
5 Returns the largest prime if there's a tie in frequency, or -1 if no primes exist.
6 """
7
8 def is_prime(num: int) -> bool:
9 """Check if a number greater than 10 is prime."""
10 if num <= 1:
11 return False
12 if num == 2:
13 return True
14 if num % 2 == 0:
15 return False
16 # Check odd divisors up to sqrt(num)
17 return all(num % divisor != 0 for divisor in range(3, int(num**0.5) + 1, 2))
18
19 rows, cols = len(mat), len(mat[0])
20 prime_frequency = {}
21
22 # Iterate through each cell as a starting point
23 for start_row in range(rows):
24 for start_col in range(cols):
25 # Try all 8 directions (horizontal, vertical, diagonal)
26 for row_delta in range(-1, 2):
27 for col_delta in range(-1, 2):
28 # Skip the case where we don't move
29 if row_delta == 0 and col_delta == 0:
30 continue
31
32 # Initialize position and number
33 current_row = start_row + row_delta
34 current_col = start_col + col_delta
35 number = mat[start_row][start_col]
36
37 # Traverse in the current direction while within bounds
38 while 0 <= current_row < rows and 0 <= current_col < cols:
39 # Append the current digit to form a larger number
40 number = number * 10 + mat[current_row][current_col]
41
42 # Check if the formed number is prime (only check numbers > 10)
43 if number > 10 and is_prime(number):
44 prime_frequency[number] = prime_frequency.get(number, 0) + 1
45
46 # Move to next position in the same direction
47 current_row += row_delta
48 current_col += col_delta
49
50 # Find the most frequent prime (largest if tie)
51 result = -1
52 max_frequency = 0
53
54 for prime_num, frequency in prime_frequency.items():
55 if frequency > max_frequency:
56 max_frequency = frequency
57 result = prime_num
58 elif frequency == max_frequency:
59 result = max(result, prime_num)
60
61 return result
62
1class Solution {
2 public int mostFrequentPrime(int[][] mat) {
3 int rows = mat.length;
4 int cols = mat[0].length;
5
6 // Map to store frequency count of each prime number found
7 Map<Integer, Integer> primeFrequency = new HashMap<>();
8
9 // Iterate through each cell in the matrix as starting point
10 for (int row = 0; row < rows; ++row) {
11 for (int col = 0; col < cols; ++col) {
12
13 // Try all 8 directions from current cell
14 for (int rowDirection = -1; rowDirection <= 1; ++rowDirection) {
15 for (int colDirection = -1; colDirection <= 1; ++colDirection) {
16
17 // Skip the case where we don't move (both directions are 0)
18 if (rowDirection == 0 && colDirection == 0) {
19 continue;
20 }
21
22 // Initialize position and value for traversal
23 int currentRow = row + rowDirection;
24 int currentCol = col + colDirection;
25 int currentValue = mat[row][col];
26
27 // Traverse in the current direction while within bounds
28 while (currentRow >= 0 && currentRow < rows &&
29 currentCol >= 0 && currentCol < cols) {
30
31 // Append the digit at current position to form new number
32 currentValue = currentValue * 10 + mat[currentRow][currentCol];
33
34 // If the formed number is prime, update its frequency
35 if (isPrime(currentValue)) {
36 primeFrequency.merge(currentValue, 1, Integer::sum);
37 }
38
39 // Move to next position in the same direction
40 currentRow += rowDirection;
41 currentCol += colDirection;
42 }
43 }
44 }
45 }
46 }
47
48 // Find the prime with maximum frequency (or largest value if tied)
49 int result = -1;
50 int maxFrequency = 0;
51
52 for (Map.Entry<Integer, Integer> entry : primeFrequency.entrySet()) {
53 int primeValue = entry.getKey();
54 int frequency = entry.getValue();
55
56 // Update result if we found higher frequency or same frequency with larger prime
57 if (maxFrequency < frequency || (maxFrequency == frequency && result < primeValue)) {
58 maxFrequency = frequency;
59 result = primeValue;
60 }
61 }
62
63 return result;
64 }
65
66 /**
67 * Checks if a number is prime
68 * @param n the number to check
69 * @return true if n is prime, false otherwise
70 */
71 private boolean isPrime(int n) {
72 // Check divisibility up to square root of n
73 for (int i = 2; i <= n / i; ++i) {
74 if (n % i == 0) {
75 return false;
76 }
77 }
78 return true;
79 }
80}
81
1class Solution {
2public:
3 int mostFrequentPrime(vector<vector<int>>& mat) {
4 int rows = mat.size();
5 int cols = mat[0].size();
6 unordered_map<int, int> primeFrequency;
7
8 // Iterate through each cell in the matrix as a starting point
9 for (int row = 0; row < rows; ++row) {
10 for (int col = 0; col < cols; ++col) {
11 // Try all 8 directions from current cell
12 for (int deltaRow = -1; deltaRow <= 1; ++deltaRow) {
13 for (int deltaCol = -1; deltaCol <= 1; ++deltaCol) {
14 // Skip the case where we don't move (both deltas are 0)
15 if (deltaRow == 0 && deltaCol == 0) {
16 continue;
17 }
18
19 // Start moving in the chosen direction
20 int currentRow = row + deltaRow;
21 int currentCol = col + deltaCol;
22 int number = mat[row][col]; // Start with the digit at starting position
23
24 // Continue in the same direction while within bounds
25 while (currentRow >= 0 && currentRow < rows &&
26 currentCol >= 0 && currentCol < cols) {
27 // Append the current digit to form a larger number
28 number = number * 10 + mat[currentRow][currentCol];
29
30 // Check if the formed number is prime and update frequency
31 if (isPrime(number)) {
32 primeFrequency[number]++;
33 }
34
35 // Move to next position in the same direction
36 currentRow += deltaRow;
37 currentCol += deltaCol;
38 }
39 }
40 }
41 }
42 }
43
44 // Find the prime number with maximum frequency
45 // If there's a tie, choose the larger prime number
46 int result = -1;
47 int maxFrequency = 0;
48
49 for (auto& [primeNumber, frequency] : primeFrequency) {
50 if (maxFrequency < frequency ||
51 (maxFrequency == frequency && result < primeNumber)) {
52 maxFrequency = frequency;
53 result = primeNumber;
54 }
55 }
56
57 return result;
58 }
59
60private:
61 // Helper function to check if a number is prime
62 bool isPrime(int n) {
63 // Check divisibility up to sqrt(n)
64 for (int divisor = 2; divisor * divisor <= n; ++divisor) {
65 if (n % divisor == 0) {
66 return false;
67 }
68 }
69 return true;
70 }
71};
72
1/**
2 * Finds the most frequent prime number formed by traversing the matrix in all 8 directions
3 * @param mat - The input matrix of digits
4 * @returns The most frequent prime number, or -1 if no prime is found
5 */
6function mostFrequentPrime(mat: number[][]): number {
7 const rows: number = mat.length;
8 const cols: number = mat[0].length;
9 // Map to store prime numbers and their frequencies
10 const primeFrequencyMap: Map<number, number> = new Map();
11
12 /**
13 * Checks if a number is prime
14 * @param num - The number to check
15 * @returns True if the number is prime, false otherwise
16 */
17 const isPrime = (num: number): boolean => {
18 // Check divisibility up to square root of num
19 for (let divisor = 2; divisor * divisor <= num; ++divisor) {
20 if (num % divisor === 0) {
21 return false;
22 }
23 }
24 return true;
25 };
26
27 // Iterate through each cell in the matrix as a starting point
28 for (let row = 0; row < rows; ++row) {
29 for (let col = 0; col < cols; ++col) {
30 // Try all 8 directions (horizontal, vertical, and diagonal)
31 for (let rowDirection = -1; rowDirection <= 1; ++rowDirection) {
32 for (let colDirection = -1; colDirection <= 1; ++colDirection) {
33 // Skip the case where both directions are 0 (no movement)
34 if (rowDirection === 0 && colDirection === 0) {
35 continue;
36 }
37
38 // Initialize current position and value
39 let currentRow: number = row + rowDirection;
40 let currentCol: number = col + colDirection;
41 let currentValue: number = mat[row][col];
42
43 // Traverse in the current direction while within bounds
44 while (currentRow >= 0 && currentRow < rows &&
45 currentCol >= 0 && currentCol < cols) {
46 // Append the current digit to form a new number
47 currentValue = currentValue * 10 + mat[currentRow][currentCol];
48
49 // If the formed number is prime, update its frequency
50 if (isPrime(currentValue)) {
51 primeFrequencyMap.set(currentValue,
52 (primeFrequencyMap.get(currentValue) || 0) + 1);
53 }
54
55 // Move to the next position in the current direction
56 currentRow += rowDirection;
57 currentCol += colDirection;
58 }
59 }
60 }
61 }
62 }
63
64 // Find the prime with maximum frequency (ties broken by larger prime value)
65 let mostFrequentPrime: number = -1;
66 let maxFrequency: number = 0;
67
68 primeFrequencyMap.forEach((frequency: number, primeNumber: number) => {
69 // Update if we found a higher frequency, or same frequency but larger prime
70 if (maxFrequency < frequency ||
71 (maxFrequency === frequency && mostFrequentPrime < primeNumber)) {
72 maxFrequency = frequency;
73 mostFrequentPrime = primeNumber;
74 }
75 });
76
77 return mostFrequentPrime;
78}
79
Time and Space Complexity
Time Complexity: O(m × n × max(m, n) × 10^(max(m, n)/2))
The analysis breaks down as follows:
- The outer two loops iterate through all cells in the matrix:
O(m × n)
- For each cell, we explore 8 directions (excluding the case where both a and b are 0):
O(8)
=O(1)
- In each direction, we traverse at most
max(m, n)
cells before going out of bounds:O(max(m, n))
- For each number formed during traversal, we check if it's prime using the
is_prime
function - The largest number that can be formed has at most
max(m, n)
digits, making it at most10^max(m, n)
- The
is_prime
function runs inO(sqrt(v))
time, wherev
is the value being checked - For a number with
max(m, n)
digits,sqrt(10^max(m, n)) = 10^(max(m, n)/2)
Therefore, the overall time complexity is O(m × n × max(m, n) × 10^(max(m, n)/2))
.
Space Complexity: O(m × n × max(m, n))
The space complexity is dominated by the Counter
object cnt
, which stores the frequency of prime numbers found:
- From each starting cell, we can explore 8 directions
- In each direction, we can form at most
max(m, n) - 1
different numbers (excluding single digits) - With
m × n
starting cells, the maximum number of unique prime numbers that could be stored isO(m × n × max(m, n))
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Counting Intermediate Numbers During Traversal
A critical mistake is only counting the final number at the end of a traversal, rather than counting all numbers formed along the way.
Incorrect approach:
# Wrong: Only counts the final number after traversing to boundary while 0 <= current_row < rows and 0 <= current_col < cols: number = number * 10 + mat[current_row][current_col] current_row += row_delta current_col += col_delta # Only check once after loop ends if number > 10 and is_prime(number): prime_frequency[number] = prime_frequency.get(number, 0) + 1
Correct approach:
# Right: Check and count every number formed during traversal while 0 <= current_row < rows and 0 <= current_col < cols: number = number * 10 + mat[current_row][current_col] # Check immediately after forming each new number if number > 10 and is_prime(number): prime_frequency[number] = prime_frequency.get(number, 0) + 1 current_row += row_delta current_col += col_delta
2. Forgetting to Check Single-Digit Starting Numbers
Some implementations might skip checking if the starting cell itself forms a valid prime when combined with the next cell.
Incorrect approach:
# Wrong: Starts with empty number and skips first cell's contribution number = 0 # Starting with 0 instead of mat[start_row][start_col] current_row = start_row + row_delta current_col = start_col + col_delta
Correct approach:
# Right: Start with the digit from the starting cell number = mat[start_row][start_col] current_row = start_row + row_delta current_col = start_col + col_delta
3. Inefficient Prime Checking
Using a naive prime check that tests all numbers up to n-1 can cause timeouts for large numbers.
Inefficient approach:
def is_prime(num):
if num <= 1:
return False
# Wrong: Checking all numbers up to num-1
for i in range(2, num):
if num % i == 0:
return False
return True
Efficient approach:
def is_prime(num):
if num <= 1:
return False
if num == 2:
return True
if num % 2 == 0:
return False
# Right: Only check up to square root
return all(num % divisor != 0 for divisor in range(3, int(num**0.5) + 1, 2))
4. Integer Overflow with Large Numbers
When traversing long paths, numbers can grow exponentially (e.g., a 10x10 matrix could create numbers with up to 10 digits). While Python handles big integers automatically, other languages might face overflow issues.
Solution for languages with integer limits:
- Add a maximum digit limit check
- Stop traversing when the number exceeds a reasonable threshold
- Use appropriate data types (long long in C++, BigInteger in Java)
# Add safeguard against extremely large numbers
MAX_DIGITS = 10 # or any reasonable limit
while 0 <= current_row < rows and 0 <= current_col < cols:
number = number * 10 + mat[current_row][current_col]
if len(str(number)) > MAX_DIGITS:
break # Stop if number gets too large
if number > 10 and is_prime(number):
prime_frequency[number] = prime_frequency.get(number, 0) + 1
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!