🧠
Backtracking with Early Pruning Using Character Frequency
💡 This approach improves brute force by quickly rejecting impossible searches based on character counts, reducing unnecessary DFS calls.
Intuition
Before searching, count characters in the board and the word. If the board lacks enough occurrences of any character, return false immediately.
Algorithm
- Count frequency of each character in the board and in the word.
- If any character in the word appears more times than in the board, return false immediately.
- Otherwise, perform the same DFS backtracking as brute force.
- Optionally, reverse the word if the last character is rarer than the first to reduce search space.
💡 This pruning step can save a lot of time by avoiding DFS when the word is impossible to form.
from typing import List
from collections import Counter
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
rows, cols = len(board), len(board[0])
board_count = Counter(c for row in board for c in row)
word_count = Counter(word)
for c in word_count:
if word_count[c] > board_count.get(c, 0):
return False
# Optional: reverse word if last char rarer than first
if board_count[word[0]] > board_count[word[-1]]:
word = word[::-1]
def dfs(r, c, index):
if index == len(word):
return True
if r < 0 or r >= rows or c < 0 or c >= cols or board[r][c] != word[index]:
return False
temp = board[r][c]
board[r][c] = '#'
found = (dfs(r+1, c, index+1) or dfs(r-1, c, index+1) or
dfs(r, c+1, index+1) or dfs(r, c-1, index+1))
board[r][c] = temp
return found
for i in range(rows):
for j in range(cols):
if board[i][j] == word[0] and dfs(i, j, 0):
return True
return False
# Example usage:
# sol = Solution()
# print(sol.exist([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABCCED')) # True
Line Notes
board_count = Counter(c for row in board for c in row)Count all characters in the board
word_count = Counter(word)Count characters in the word
for c in word_count:Check if board has enough of each character
if word_count[c] > board_count.get(c, 0):If board lacks characters, return false early
if board_count[word[0]] > board_count[word[-1]]:Heuristic to reverse word if last char is rarer
def dfs(r, c, index):DFS helper function same as brute force
temp = board[r][c]Save cell before marking visited
board[r][c] = '#'Mark visited
found = (dfs(...) or ... )Explore all 4 directions
board[r][c] = tempBacktrack by restoring cell
import java.util.*;
public class Solution {
public boolean exist(char[][] board, String word) {
int rows = board.length, cols = board[0].length;
Map<Character, Integer> boardCount = new HashMap<>();
for (char[] row : board) {
for (char c : row) {
boardCount.put(c, boardCount.getOrDefault(c, 0) + 1);
}
}
Map<Character, Integer> wordCount = new HashMap<>();
for (char c : word.toCharArray()) {
wordCount.put(c, wordCount.getOrDefault(c, 0) + 1);
}
for (char c : wordCount.keySet()) {
if (wordCount.get(c) > boardCount.getOrDefault(c, 0)) return false;
}
if (boardCount.get(word.charAt(0)) > boardCount.get(word.charAt(word.length() - 1))) {
word = new StringBuilder(word).reverse().toString();
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (board[i][j] == word.charAt(0) && dfs(board, word, i, j, 0)) {
return true;
}
}
}
return false;
}
private boolean dfs(char[][] board, String word, int r, int c, int index) {
if (index == word.length()) return true;
if (r < 0 || r >= board.length || c < 0 || c >= board[0].length || board[r][c] != word.charAt(index))
return false;
char temp = board[r][c];
board[r][c] = '#';
boolean found = dfs(board, word, r + 1, c, index + 1) ||
dfs(board, word, r - 1, c, index + 1) ||
dfs(board, word, r, c + 1, index + 1) ||
dfs(board, word, r, c - 1, index + 1);
board[r][c] = temp;
return found;
}
// Example usage:
// public static void main(String[] args) {
// Solution sol = new Solution();
// char[][] board = {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}};
// System.out.println(sol.exist(board, "ABCCED")); // true
// }
}
Line Notes
Map<Character, Integer> boardCount = new HashMap<>();Count characters in board
for (char c : word.toCharArray())Count characters in word
if (wordCount.get(c) > boardCount.getOrDefault(c, 0)) return false;Early pruning if board lacks chars
if (boardCount.get(word.charAt(0)) > boardCount.get(word.charAt(word.length() - 1)))Heuristic to reverse word
private boolean dfs(char[][] board, String word, int r, int c, int index)DFS helper same as brute force
char temp = board[r][c];Save cell before marking visited
board[r][c] = '#';Mark visited
boolean found = dfs(...) || ...Explore all 4 directions
board[r][c] = temp;Backtrack by restoring cell
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
unordered_map<char, int> boardCount, wordCount;
for (auto& row : board) {
for (char c : row) boardCount[c]++;
}
for (char c : word) wordCount[c]++;
for (auto& p : wordCount) {
if (boardCount[p.first] < p.second) return false;
}
if (boardCount[word[0]] > boardCount[word.back()]) {
reverse(word.begin(), word.end());
}
int rows = board.size(), cols = board[0].size();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (board[i][j] == word[0] && dfs(board, word, i, j, 0))
return true;
}
}
return false;
}
private:
bool dfs(vector<vector<char>>& board, const string& word, int r, int c, int index) {
if (index == (int)word.size()) return true;
if (r < 0 || r >= (int)board.size() || c < 0 || c >= (int)board[0].size() || board[r][c] != word[index])
return false;
char temp = board[r][c];
board[r][c] = '#';
bool found = dfs(board, word, r + 1, c, index + 1) ||
dfs(board, word, r - 1, c, index + 1) ||
dfs(board, word, r, c + 1, index + 1) ||
dfs(board, word, r, c - 1, index + 1);
board[r][c] = temp;
return found;
}
// Example usage:
// int main() {
// Solution sol;
// vector<vector<char>> board = {{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}};
// bool res = sol.exist(board, "ABCCED");
// return 0;
// }
Line Notes
unordered_map<char, int> boardCount, wordCount;Count characters in board and word
for (auto& p : wordCount)Check if board has enough characters
if (boardCount[word[0]] > boardCount[word.back()])Heuristic to reverse word
bool dfs(vector<vector<char>>& board, const string& word, int r, int c, int index)DFS helper same as brute force
char temp = board[r][c];Save cell before marking visited
board[r][c] = '#';Mark visited
bool found = dfs(...) || ...Explore all 4 directions
board[r][c] = temp;Backtrack by restoring cell
var exist = function(board, word) {
const rows = board.length, cols = board[0].length;
const boardCount = {};
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
boardCount[board[r][c]] = (boardCount[board[r][c]] || 0) + 1;
}
}
const wordCount = {};
for (const ch of word) {
wordCount[ch] = (wordCount[ch] || 0) + 1;
}
for (const ch in wordCount) {
if (!boardCount[ch] || wordCount[ch] > boardCount[ch]) return false;
}
if (boardCount[word[0]] > boardCount[word[word.length - 1]]) {
word = word.split('').reverse().join('');
}
function dfs(r, c, index) {
if (index === word.length) return true;
if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] !== word[index]) return false;
const temp = board[r][c];
board[r][c] = '#';
const found = dfs(r + 1, c, index + 1) || dfs(r - 1, c, index + 1) || dfs(r, c + 1, index + 1) || dfs(r, c - 1, index + 1);
board[r][c] = temp;
return found;
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
if (board[i][j] === word[0] && dfs(i, j, 0)) return true;
}
}
return false;
};
// Example usage:
// console.log(exist([['A','B','C','E'],['S','F','C','S'],['A','D','E','E']], 'ABCCED')); // true
Line Notes
const boardCount = {};Count characters in board
const wordCount = {};Count characters in word
if (!boardCount[ch] || wordCount[ch] > boardCount[ch]) return false;Early pruning if board lacks chars
if (boardCount[word[0]] > boardCount[word[word.length - 1]])Heuristic to reverse word
function dfs(r, c, index)DFS helper same as brute force
const temp = board[r][c];Save cell before marking visited
board[r][c] = '#';Mark visited
const found = dfs(...) || ...Explore all 4 directions
board[r][c] = temp;Backtrack by restoring cell
TimeStill O(N * 3^L) but often faster due to pruning
SpaceO(L) recursion stack
Pruning avoids many DFS calls when word characters are missing or insufficient in the board
💡 This optimization can drastically reduce runtime on impossible cases, making it practical for larger inputs.
Interview Verdict: Accepted and faster than brute force on many inputs
This approach is a practical improvement and often sufficient in interviews.