Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
You are given an m x n matrix grid consisting of characters and a string pattern.
A horizontal substring is a contiguous sequence of characters read from left to right. If the end of a row is reached before the substring is complete, it wraps to the first column of the next row and continues as needed. You do not wrap from the bottom row back to the top.
A vertical substring is a contiguous sequence of characters read from top to bottom. If the bottom of a column is reached before the substring is complete, it wraps to the first row of the next column and continues as needed. You do not wrap from the last column back to the first.
Count the number of cells in the matrix that satisfy the following condition:
pattern.Return the count of these cells.
Example 1:
Input: grid = [["a","a","c","c"],["b","b","b","c"],["a","a","b","a"],["c","a","a","c"],["a","a","b","a"]], pattern = "abaca"
Output: 1
Explanation:
The pattern "abaca" appears once as a horizontal substring (colored blue) and once as a vertical substring (colored red), intersecting at one cell (colored purple).
Example 2:
Input: grid = [["c","a","a","a"],["a","a","b","a"],["b","b","a","a"],["a","a","b","a"]], pattern = "aba"
Output: 4
Explanation:
The cells colored above are all part of at least one horizontal and one vertical substring matching the pattern "aba".
Example 3:
Input: grid = [["a"]], pattern = "a"
Output: 1
Constraints:
m == grid.lengthn == grid[i].length1 <= m, n <= 10001 <= m * n <= 1051 <= pattern.length <= m * ngrid and pattern consist of only lowercase English letters.Problem summary: You are given an m x n matrix grid consisting of characters and a string pattern. A horizontal substring is a contiguous sequence of characters read from left to right. If the end of a row is reached before the substring is complete, it wraps to the first column of the next row and continues as needed. You do not wrap from the bottom row back to the top. A vertical substring is a contiguous sequence of characters read from top to bottom. If the bottom of a column is reached before the substring is complete, it wraps to the first row of the next column and continues as needed. You do not wrap from the last column back to the first. Count the number of cells in the matrix that satisfy the following condition: The cell must be part of at least one horizontal substring and at least one vertical substring, where both substrings are equal to the given pattern. Return the count of these cells.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · String Matching
[["a","a","c","c"],["b","b","b","c"],["a","a","b","a"],["c","a","a","c"],["a","a","b","a"]] "abaca"
[["c","a","a","a"],["a","a","b","a"],["b","b","a","a"],["a","a","b","a"]] "aba"
[["a"]] "a"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
class Solution {
public int countCells(char[][] grid, String pattern) {
final int m = grid.length;
final int n = grid[0].length;
int ans = 0;
StringBuilder flattenedGridRow = new StringBuilder();
StringBuilder flattenedGridCol = new StringBuilder();
// Flatten the grid for horizontal matching.
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
flattenedGridRow.append(grid[i][j]);
// Flatten the grid for vertical matching.
for (int j = 0; j < n; ++j)
for (int i = 0; i < m; ++i)
flattenedGridCol.append(grid[i][j]);
// Find matching positions.
boolean[][] horizontalMatches =
markMatchedCells(flattenedGridRow.toString(), pattern, m, n, true);
boolean[][] verticalMatches =
markMatchedCells(flattenedGridCol.toString(), pattern, m, n, false);
// Count overlapping match positions.
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
if (horizontalMatches[i][j] && verticalMatches[i][j])
++ans;
return ans;
}
private static final long BASE = 13;
private static final long HASH = 1_000_000_007;
private boolean[][] markMatchedCells(final String flattenedGrid, final String pattern, int m,
int n, boolean isHorizontal) {
boolean[][] matchMatrix = new boolean[m][n];
int[] matchPrefix = new int[flattenedGrid.length() + 1];
long[] pows = new long[pattern.length()]; // pows[i] := BASE^i % HASH
pows[0] = 1;
long patternHash = 0;
long runningHash = 0;
for (int i = 1; i < pattern.length(); ++i)
pows[i] = (pows[i - 1] * BASE) % HASH;
for (final char c : pattern.toCharArray())
patternHash = (patternHash * BASE + (c - 'a')) % HASH;
for (int i = 0; i < flattenedGrid.length(); ++i) {
runningHash = (runningHash * BASE + (flattenedGrid.charAt(i) - 'a')) % HASH;
if (i >= pattern.length() - 1) {
if (runningHash == patternHash) { // Match found.
++matchPrefix[i - pattern.length() + 1];
--matchPrefix[i + 1];
}
// Remove the contribution of the oldest letter.
final long oldestLetterHash =
(pows[pattern.length() - 1] * (flattenedGrid.charAt(i - pattern.length() + 1) - 'a')) %
HASH;
runningHash = (runningHash - oldestLetterHash + HASH) % HASH;
}
}
for (int k = 0; k < flattenedGrid.length(); ++k) {
matchPrefix[k] += (k > 0) ? matchPrefix[k - 1] : 0;
if (matchPrefix[k] > 0) {
final int i = isHorizontal ? k / n : k % m;
final int j = isHorizontal ? k % n : k / m;
matchMatrix[i][j] = true;
}
}
return matchMatrix;
}
}
// Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
package main
import "slices"
// https://space.bilibili.com/206214
func calcPi(pattern string) []int {
pi := make([]int, len(pattern))
match := 0
for i := 1; i < len(pi); i++ {
b := pattern[i]
for match > 0 && pattern[match] != b {
match = pi[match-1]
}
if pattern[match] == b {
match++
}
pi[i] = match
}
return pi
}
func kmpSearch(text []byte, pattern string, pi []int) []int {
match := 0
n := len(text)
diff := make([]int, n+1)
for i, b := range text {
for match > 0 && pattern[match] != b {
match = pi[match-1]
}
if pattern[match] == b {
match++
}
if match == len(pi) {
diff[i-len(pi)+1]++
diff[i+1]--
match = pi[match-1]
}
}
for i := 1; i < n; i++ {
diff[i] += diff[i-1]
}
return diff[:n]
}
func countCells(grid [][]byte, pattern string) (ans int) {
hText := slices.Concat(grid...)
m, n := len(grid), len(grid[0])
vText := make([]byte, 0, m*n)
for j := range n {
for _, row := range grid {
vText = append(vText, row[j])
}
}
pi := calcPi(pattern)
inPatternH := kmpSearch(hText, pattern, pi)
inPatternV := kmpSearch(vText, pattern, pi)
for i, x := range inPatternH {
if x > 0 && inPatternV[i%n*m+i/n] > 0 {
ans++
}
}
return
}
# Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
class Solution:
def countCells(self, grid: list[list[str]], pattern: str) -> int:
BASE = 13
HASH = 1_000_000_007
m = len(grid)
n = len(grid[0])
def markMatchedCells(flattenedGrid: str, isHorizontal: bool) -> list[list[bool]]:
matchMatrix = [[False] * n for _ in range(m)]
matchPrefix = [0] * (len(flattenedGrid) + 1)
pows = [1] # pows[i] := BASE^i % HASH
patternHash = 0
runningHash = 0
for i in range(1, len(pattern)):
pows.append((pows[-1] * BASE) % HASH)
for c in pattern:
patternHash = (patternHash * BASE + (ord(c) - ord('a'))) % HASH
for i in range(len(flattenedGrid)):
runningHash = (
runningHash * BASE + (ord(flattenedGrid[i]) - ord('a'))) % HASH
if i >= len(pattern) - 1:
if runningHash == patternHash: # Match found.
matchPrefix[i - len(pattern) + 1] += 1
matchPrefix[i + 1] -= 1
# Remove the contribution of the oldest letter.
oldestLetterHash = (
pows[len(pattern) - 1] *
(ord(flattenedGrid[i - len(pattern) + 1]) - ord('a'))) % HASH
runningHash = (runningHash - oldestLetterHash + HASH) % HASH
for k in range(len(flattenedGrid)):
if k > 0:
matchPrefix[k] += matchPrefix[k - 1]
if matchPrefix[k] > 0:
i = k // n if isHorizontal else k % m
j = k % n if isHorizontal else k // m
matchMatrix[i][j] = True
return matchMatrix
# Find matching positions.
flattenedGridRow = ''.join(cell for row in grid for cell in row)
flattenedGridCol = ''.join(cell for col in zip(*grid) for cell in col)
horizontalMatches = markMatchedCells(flattenedGridRow, True)
verticalMatches = markMatchedCells(flattenedGridCol, False)
return sum(horizontalMatches[i][j] and verticalMatches[i][j]
for i in range(m)
for j in range(n))
// Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
// class Solution {
// public int countCells(char[][] grid, String pattern) {
// final int m = grid.length;
// final int n = grid[0].length;
// int ans = 0;
// StringBuilder flattenedGridRow = new StringBuilder();
// StringBuilder flattenedGridCol = new StringBuilder();
//
// // Flatten the grid for horizontal matching.
// for (int i = 0; i < m; ++i)
// for (int j = 0; j < n; ++j)
// flattenedGridRow.append(grid[i][j]);
//
// // Flatten the grid for vertical matching.
// for (int j = 0; j < n; ++j)
// for (int i = 0; i < m; ++i)
// flattenedGridCol.append(grid[i][j]);
//
// // Find matching positions.
// boolean[][] horizontalMatches =
// markMatchedCells(flattenedGridRow.toString(), pattern, m, n, true);
// boolean[][] verticalMatches =
// markMatchedCells(flattenedGridCol.toString(), pattern, m, n, false);
//
// // Count overlapping match positions.
// for (int i = 0; i < m; ++i)
// for (int j = 0; j < n; ++j)
// if (horizontalMatches[i][j] && verticalMatches[i][j])
// ++ans;
//
// return ans;
// }
//
// private static final long BASE = 13;
// private static final long HASH = 1_000_000_007;
//
// private boolean[][] markMatchedCells(final String flattenedGrid, final String pattern, int m,
// int n, boolean isHorizontal) {
// boolean[][] matchMatrix = new boolean[m][n];
// int[] matchPrefix = new int[flattenedGrid.length() + 1];
// long[] pows = new long[pattern.length()]; // pows[i] := BASE^i % HASH
// pows[0] = 1;
// long patternHash = 0;
// long runningHash = 0;
//
// for (int i = 1; i < pattern.length(); ++i)
// pows[i] = (pows[i - 1] * BASE) % HASH;
//
// for (final char c : pattern.toCharArray())
// patternHash = (patternHash * BASE + (c - 'a')) % HASH;
//
// for (int i = 0; i < flattenedGrid.length(); ++i) {
// runningHash = (runningHash * BASE + (flattenedGrid.charAt(i) - 'a')) % HASH;
// if (i >= pattern.length() - 1) {
// if (runningHash == patternHash) { // Match found.
// ++matchPrefix[i - pattern.length() + 1];
// --matchPrefix[i + 1];
// }
// // Remove the contribution of the oldest letter.
// final long oldestLetterHash =
// (pows[pattern.length() - 1] * (flattenedGrid.charAt(i - pattern.length() + 1) - 'a')) %
// HASH;
// runningHash = (runningHash - oldestLetterHash + HASH) % HASH;
// }
// }
//
// for (int k = 0; k < flattenedGrid.length(); ++k) {
// matchPrefix[k] += (k > 0) ? matchPrefix[k - 1] : 0;
// if (matchPrefix[k] > 0) {
// final int i = isHorizontal ? k / n : k % m;
// final int j = isHorizontal ? k % n : k / m;
// matchMatrix[i][j] = true;
// }
// }
//
// return matchMatrix;
// }
// }
// Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #3529: Count Cells in Overlapping Horizontal and Vertical Substrings
// class Solution {
// public int countCells(char[][] grid, String pattern) {
// final int m = grid.length;
// final int n = grid[0].length;
// int ans = 0;
// StringBuilder flattenedGridRow = new StringBuilder();
// StringBuilder flattenedGridCol = new StringBuilder();
//
// // Flatten the grid for horizontal matching.
// for (int i = 0; i < m; ++i)
// for (int j = 0; j < n; ++j)
// flattenedGridRow.append(grid[i][j]);
//
// // Flatten the grid for vertical matching.
// for (int j = 0; j < n; ++j)
// for (int i = 0; i < m; ++i)
// flattenedGridCol.append(grid[i][j]);
//
// // Find matching positions.
// boolean[][] horizontalMatches =
// markMatchedCells(flattenedGridRow.toString(), pattern, m, n, true);
// boolean[][] verticalMatches =
// markMatchedCells(flattenedGridCol.toString(), pattern, m, n, false);
//
// // Count overlapping match positions.
// for (int i = 0; i < m; ++i)
// for (int j = 0; j < n; ++j)
// if (horizontalMatches[i][j] && verticalMatches[i][j])
// ++ans;
//
// return ans;
// }
//
// private static final long BASE = 13;
// private static final long HASH = 1_000_000_007;
//
// private boolean[][] markMatchedCells(final String flattenedGrid, final String pattern, int m,
// int n, boolean isHorizontal) {
// boolean[][] matchMatrix = new boolean[m][n];
// int[] matchPrefix = new int[flattenedGrid.length() + 1];
// long[] pows = new long[pattern.length()]; // pows[i] := BASE^i % HASH
// pows[0] = 1;
// long patternHash = 0;
// long runningHash = 0;
//
// for (int i = 1; i < pattern.length(); ++i)
// pows[i] = (pows[i - 1] * BASE) % HASH;
//
// for (final char c : pattern.toCharArray())
// patternHash = (patternHash * BASE + (c - 'a')) % HASH;
//
// for (int i = 0; i < flattenedGrid.length(); ++i) {
// runningHash = (runningHash * BASE + (flattenedGrid.charAt(i) - 'a')) % HASH;
// if (i >= pattern.length() - 1) {
// if (runningHash == patternHash) { // Match found.
// ++matchPrefix[i - pattern.length() + 1];
// --matchPrefix[i + 1];
// }
// // Remove the contribution of the oldest letter.
// final long oldestLetterHash =
// (pows[pattern.length() - 1] * (flattenedGrid.charAt(i - pattern.length() + 1) - 'a')) %
// HASH;
// runningHash = (runningHash - oldestLetterHash + HASH) % HASH;
// }
// }
//
// for (int k = 0; k < flattenedGrid.length(); ++k) {
// matchPrefix[k] += (k > 0) ? matchPrefix[k - 1] : 0;
// if (matchPrefix[k] > 0) {
// final int i = isHorizontal ? k / n : k % m;
// final int j = isHorizontal ? k % n : k / m;
// matchMatrix[i][j] = true;
// }
// }
//
// return matchMatrix;
// }
// }
Use this to step through a reusable interview workflow for this problem.
At each of the n starting positions in the text, compare up to m characters with the pattern. If a mismatch occurs, shift by one and restart. Worst case (e.g., searching "aab" in "aaaa...a") checks m characters at nearly every position: O(n × m).
KMP and Z-algorithm preprocess the pattern in O(m) to build a failure/Z-array, then scan the text in O(n) — never backtracking. Total: O(n + m). Rabin-Karp uses rolling hashes for O(n + m) expected time. All beat the O(n × m) brute force of checking every position.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.