Skip to content
dsa450for five year olds

String · 23 of 43

Count of number of given string in 2D character array

Count every way to spell a word on a letter grid by stepping between touching boxes, never using a box twice, and stepping back at dead ends.

Simple way
time O(R * C * 3^L * L)
room O(L)
Clever way
time O(R * C * 3^L)
room O(L)

The problem, simply

You get a grid of letters with R rows and C columns, and a word of length L. Count how many different paths spell the word.

A path starts on any box and steps to a box that touches it on a side: up, down, left or right. It may turn at every step, like a snake. It may not use the same box twice. Two paths are different if they use different boxes.

This is the bending version. The sister problem "Search a word in a 2D grid" only reads in straight lines.

A story to remember it

Picture it

grid
012012CAXAXTTAC

CAT can be spelled 2 ways here: C(0,0) A(1,0) T(2,0), and C(2,2) A(2,1) T(2,0). Both paths share the T, and that is fine, they are different paths.

  • answer
  • path

Examples

The classic grid

In
grid = [DDDGDD, BBDEBS, BSKEBK, DDDDDE, DDDDDE, DDDDDG], word = 'GEEKS'
Out
2

From the G at (0,3): down to E, down to E, then turn left to K and S. And from the G in the bottom right corner, straight up through E, E, K and S.

A small grid

In
grid = [CAX, AXT, TAC], word = 'CAT'
Out
2

One path from the top left C and one from the bottom right C. The top A leads to a dead end.

A box cannot be reused

In
grid = [AB, XX], word = 'ABA'
Out
0

Spelling ABA would need to hop back onto the same A. That is not allowed.

One letter word

In
grid = [AB, BA], word = 'A'
Out
2

Each box holding A is a path of length one.

Not there at all

In
grid = [AB, CD], word = 'DOG'
Out
0

No box holds O, so no path can be finished.

Way 1: the simple way

Walk every snake path of length L from every box, without looking at the letters on the way. Only when a path is L boxes long, read its letters and compare them with the word.

  1. From each box, walk every path of L boxes that never reuses a box.
  2. At full length, spell the path and compare it with the word.
  3. Count the matches.
java
class Solution {
    public int countWord(char[][] grid, String word) {
        int R = grid.length, C = grid[0].length, total = 0;
        boolean[][] used = new boolean[R][C];
        for (int r = 0; r < R; r++)
            for (int c = 0; c < C; c++)
                total += walk(grid, used, r, c, new StringBuilder(), word);
        return total;
    }

    private int walk(char[][] g, boolean[][] used, int r, int c, StringBuilder path, String word) {
        if (r < 0 || c < 0 || r >= g.length || c >= g[0].length || used[r][c]) return 0;
        path.append(g[r][c]);
        used[r][c] = true;
        int found;
        if (path.length() == word.length()) {
            found = path.toString().equals(word) ? 1 : 0;   // check only at the very end
        } else {
            found = walk(g, used, r, c + 1, path, word) + walk(g, used, r + 1, c, path, word)
                  + walk(g, used, r, c - 1, path, word) + walk(g, used, r - 1, c, path, word);
        }
        used[r][c] = false;                                 // step back
        path.deleteCharAt(path.length() - 1);
        return found;
    }
}

How long it takes: from each of the R * C boxes there are up to 4 * 3^(L-2) paths, because after the first hop you can never go straight back. Each full path is compared in O(L). So O(R * C * 3^L * L). Extra room: the path and the call stack, O(L).

Way 2: the clever way

Check letters while you hop, not at the end. If the box you step on is not the next letter of the word, stop right there. That cuts off almost every path after one or two hops.

  1. For each box, start a search with letter number k = 0.
  2. If the box is off the grid, or its letter is not word[k], return 0. A box marked with chalk holds #, so it fails this test too.
  3. If k is the last letter, return 1.
  4. Otherwise mark the box with #, add up the searches in the four directions with k + 1, then put the letter back and return the sum.

Hop, check the letter, and step back at dead ends

01 / 12

gridgrid
012012CAXAXTTAC
arrayword
012CATk
stackpath so far
empty

We look for CAT. From any box we may step up, down, left or right, but never onto a box already in our path.

  • looking now
  • done
  • answer
  • rejected
  • path
java
class Solution {
    public int countWord(char[][] grid, String word) {
        if (word.isEmpty()) return 0;
        int total = 0;
        for (int r = 0; r < grid.length; r++)
            for (int c = 0; c < grid[0].length; c++)
                total += hop(grid, word, r, c, 0);
        return total;
    }

    private int hop(char[][] g, String word, int r, int c, int k) {
        if (r < 0 || c < 0 || r >= g.length || c >= g[0].length) return 0;
        if (g[r][c] != word.charAt(k)) return 0;      // wrong letter or chalk mark: stop now
        if (k == word.length() - 1) return 1;          // spelled the whole word

        char keep = g[r][c];
        g[r][c] = '#';                                 // chalk mark: used on this path
        int found = hop(g, word, r, c + 1, k + 1) + hop(g, word, r + 1, c, k + 1)
                  + hop(g, word, r, c - 1, k + 1) + hop(g, word, r - 1, c, k + 1);
        g[r][c] = keep;                                // rub the mark out when stepping back
        return found;
    }
}

How long it takes: in the worst case, like a grid full of A and the word AAAA, every path matches and it is still O(R * C * 3^L). On normal grids most starting boxes fail at once, so it is far faster than Way 1. Extra room: the call stack is at most L deep, O(L).

Watch out for

  • Check the bounds before you read g[r][c], or the program crashes at the edges.
  • Return 1 as soon as the last letter matches. If you wait one more hop and return 1 there, a finished path is counted once for every neighbor of its last box.
  • Using # as the mark assumes the word has no #. A separate boolean[][] is safer when letters can be anything.
  • Ask whether diagonal hops are allowed. If so, add the four diagonal directions.

What interviewers ask next

  • Only say whether the word exists (LeetCode 79)? Same search, but return true the moment one path finishes.
  • Find many words at once (LeetCode 212)? Put all words in a trie and walk the grid once, following the trie as you hop. Words that share a start share the work.
  • Can you prune more? Count the letters in the grid first. If the word needs more of some letter than the grid has, return 0 at once. Also start from whichever end of the word has the rarer letter.
  • What if the word must be in a straight line? Then no backtracking is needed. Try the 8 straight directions from each box, like a word search puzzle.

Remember this

Grown-up words: hopping and stepping back is backtracking, the hops form a depth first search, the chalk mark is the visited marker, stopping on a wrong letter is pruning, and the waiting hops are the recursion call stack.