questions C

This commit is contained in:
2025-05-25 10:16:13 +01:00
parent 2123791ec3
commit e028167a47
85 changed files with 16925 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
title: Check if Word Can Be Placed In Crossword
slug: check-if-word-can-be-placed-in-crossword
difficulty: medium
leetcode_id: 2018
leetcode_url: https://leetcode.com/problems/check-if-word-can-be-placed-in-crossword/
categories:
- arrays
- strings
patterns:
- matrix-traversal
description: |
You are given an `m x n` matrix `board`, representing the **current** state of a crossword puzzle. The crossword contains lowercase English letters (from solved words), `' '` to represent any **empty** cells, and `'#'` to represent any **blocked** cells.
A word can be placed **horizontally** (left to right **or** right to left) or **vertically** (top to bottom **or** bottom to top) in the board if:
- It does not occupy a cell containing the character `'#'`.
- The cell each letter is placed in must either be `' '` (empty) or **match** the letter already on the `board`.
- There must not be any empty cells `' '` or other lowercase letters **directly left or right** of the word if the word was placed **horizontally**.
- There must not be any empty cells `' '` or other lowercase letters **directly above or below** the word if the word was placed **vertically**.
Given a string `word`, return `true` *if* `word` *can be placed in* `board`, *or* `false` *otherwise*.
constraints: |
- `m == board.length`
- `n == board[i].length`
- `1 <= m * n <= 2 * 10^5`
- `board[i][j]` will be `' '`, `'#'`, or a lowercase English letter.
- `1 <= word.length <= max(m, n)`
- `word` will contain only lowercase English letters.
examples:
- input: 'board = [["#", " ", "#"], [" ", " ", "#"], ["#", "c", " "]], word = "abc"'
output: "true"
explanation: 'The word "abc" can be placed vertically (top to bottom) starting from position (0, 1).'
- input: 'board = [[" ", "#", "a"], [" ", "#", "c"], [" ", "#", "a"]], word = "ac"'
output: "false"
explanation: "It is impossible to place the word because there will always be a space/letter above or below it."
- input: 'board = [["#", " ", "#"], [" ", " ", "#"], ["#", " ", "c"]], word = "ca"'
output: "true"
explanation: 'The word "ca" can be placed horizontally (right to left) in the last row.'
explanation:
intuition: |
Think of this problem like finding a valid "slot" in a crossword puzzle where you can write your word.
In a real crossword puzzle, a valid slot is a sequence of empty or matching cells that is **bounded** on both ends — either by a `'#'` (blocked cell) or by the edge of the grid. The slot must be exactly the right length for your word, and any pre-filled letters must match.
The key insight is that we need to check **four directions** for placing the word:
- Left to right (horizontal)
- Right to left (horizontal, reversed)
- Top to bottom (vertical)
- Bottom to top (vertical, reversed)
Rather than searching the entire grid for every possible starting position, we can systematically find all valid "slots" in each row and column. A slot is defined as a maximal sequence of non-`'#'` cells (i.e., cells that are either empty or contain a letter).
For each slot we find, we check if the word (or its reverse) fits perfectly: the slot length must equal the word length, and each cell must either be empty or match the corresponding letter in the word.
approach: |
We solve this by checking rows (horizontal placement) and columns (vertical placement) separately.
**Step 1: Define a helper function to check slots in a line**
- Given a line (a row or column), split it by `'#'` to get all candidate slots
- Each slot is a maximal sequence of non-blocked cells
- For each slot, check if the word fits (forward or backward)
&nbsp;
**Step 2: Check if word fits in a slot**
- The slot length must equal the word length
- For each position, the cell must be `' '` (empty) or match the word's letter
- Check both the word and its reverse to handle bidirectional placement
&nbsp;
**Step 3: Check all rows for horizontal placement**
- For each row, extract the cells and find all slots
- Test if the word can fit in any slot (left-to-right or right-to-left)
&nbsp;
**Step 4: Check all columns for vertical placement**
- For each column, extract the cells vertically and find all slots
- Test if the word can fit in any slot (top-to-bottom or bottom-to-top)
&nbsp;
**Step 5: Return result**
- Return `true` if any valid placement is found, `false` otherwise
common_pitfalls:
- title: Forgetting Bidirectional Placement
description: |
The word can be placed in **four** directions, not just two. A common mistake is only checking left-to-right and top-to-bottom.
For example, if the word is "cat" and you only check forward placement, you'll miss valid positions where "tac" would fit (meaning "cat" can be placed right-to-left).
Solution: Check both the word and its reverse, or equivalently, check the slot from both ends.
wrong_approach: "Only checking forward direction"
correct_approach: "Check both word and reversed word for each slot"
- title: Not Enforcing Slot Boundaries
description: |
A valid placement requires the word to occupy a **complete slot** — it must be bounded by `'#'` or the grid edge on both ends.
For example, if you have a row `[' ', ' ', ' ', '#']` and word = "ab", you cannot place "ab" at positions 0-1 because position 2 is still part of the same open slot. The slot has length 3, not 2.
This means you can't just find any two consecutive empty cells; you must find a slot whose total length equals the word length.
wrong_approach: "Looking for any substring of matching length"
correct_approach: "Split by '#' to find complete slots, then check exact length match"
- title: Ignoring Pre-filled Letters
description: |
Some cells may already contain letters from other words in the crossword. You must check that these letters **match** the corresponding letter in your word.
A common bug is treating all non-`'#'` cells as empty and overwriting them.
wrong_approach: "Treating all non-blocked cells as empty"
correct_approach: "Check that existing letters match or cell is empty"
key_takeaways:
- "**Matrix traversal with constraints**: When checking placements in a grid, consider all valid directions and boundary conditions"
- "**Slot-based thinking**: Instead of checking every starting position, identify valid slots first (bounded sequences), then verify if the word fits"
- "**Bidirectional checking**: Many grid problems require considering both forward and backward directions — factor this into your solution"
- "**Similar problems**: This pattern applies to other crossword/word-search problems like Word Search, Word Search II, and word-placement puzzles"
time_complexity: "O(m * n). We iterate through each cell of the grid at most a constant number of times — once when processing rows and once when processing columns."
space_complexity: "O(max(m, n)). We may store a row or column temporarily when checking slots, which has at most `max(m, n)` elements."
solutions:
- approach_name: Slot-Based Matching
is_optimal: true
code: |
def place_word_in_crossword(board: list[list[str]], word: str) -> bool:
m, n = len(board), len(board[0])
def can_place(slot: list[str], word: str) -> bool:
"""Check if word can be placed in slot (forward or backward)."""
if len(slot) != len(word):
return False
# Check forward placement
forward = all(
cell == ' ' or cell == ch
for cell, ch in zip(slot, word)
)
# Check backward placement
backward = all(
cell == ' ' or cell == ch
for cell, ch in zip(slot, reversed(word))
)
return forward or backward
def check_line(line: list[str]) -> bool:
"""Check all slots in a line (row or column)."""
# Split line by '#' to get all slots
slot = []
for cell in line:
if cell == '#':
# End of current slot, check it
if can_place(slot, word):
return True
slot = []
else:
slot.append(cell)
# Check final slot after last '#' (or if no '#' at all)
return can_place(slot, word)
# Check all rows (horizontal placement)
for row in board:
if check_line(row):
return True
# Check all columns (vertical placement)
for col in range(n):
column = [board[row][col] for row in range(m)]
if check_line(column):
return True
return False
explanation: |
**Time Complexity:** O(m * n) — We process each cell twice (once for rows, once for columns), and the word matching is O(word length) which is bounded by max(m, n).
**Space Complexity:** O(max(m, n)) — We store column data temporarily and maintain slots during iteration.
This approach elegantly handles all four directions by recognizing that checking a slot forward and backward covers both horizontal directions (for rows) and both vertical directions (for columns).
- approach_name: Direct Position Checking
is_optimal: false
code: |
def place_word_in_crossword(board: list[list[str]], word: str) -> bool:
m, n = len(board), len(board[0])
k = len(word)
def is_valid_start(r: int, c: int, dr: int, dc: int) -> bool:
"""Check if position (r, c) is a valid start for direction (dr, dc)."""
# Previous cell must be '#' or out of bounds
pr, pc = r - dr, c - dc
if 0 <= pr < m and 0 <= pc < n and board[pr][pc] != '#':
return False
return True
def is_valid_end(r: int, c: int, dr: int, dc: int) -> bool:
"""Check if position after word is valid (boundary or '#')."""
er, ec = r + dr * k, c + dc * k
if 0 <= er < m and 0 <= ec < n and board[er][ec] != '#':
return False
return True
def can_place_at(r: int, c: int, dr: int, dc: int) -> bool:
"""Check if word can be placed starting at (r, c) in direction (dr, dc)."""
if not is_valid_start(r, c, dr, dc):
return False
if not is_valid_end(r, c, dr, dc):
return False
# Check each letter of the word
for i in range(k):
nr, nc = r + dr * i, c + dc * i
# Must be within bounds
if not (0 <= nr < m and 0 <= nc < n):
return False
cell = board[nr][nc]
# Cell must be empty or match the letter
if cell != ' ' and cell != word[i]:
return False
return True
# Four directions: right, left, down, up
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
# Try every starting position and direction
for r in range(m):
for c in range(n):
for dr, dc in directions:
if can_place_at(r, c, dr, dc):
return True
return False
explanation: |
**Time Complexity:** O(m * n * k) — For each cell, we check 4 directions, and each check takes O(k) where k is the word length.
**Space Complexity:** O(1) — Only uses constant extra space.
This brute-force approach checks every possible starting position and direction. While correct, it's less elegant than the slot-based approach because it explicitly handles boundary conditions at every step. The slot-based approach naturally handles boundaries by splitting on `'#'`.