Imagine you're creating a word crossword puzzle where words read the same both horizontally and vertically! Given an array of unique strings words, you need to find all possible word squares you can build.
A word square is a sequence of strings where the kth row and kth column read exactly the same string. Think of it as a perfect symmetric grid where grid[i][j] == grid[j][i] for all positions.
Example: The words ["ball", "area", "lead", "lady"] form a valid word square:
b a l l
a r e a
l e a d
l a d y
Notice how column 0 reads "ball" (same as row 0), column 1 reads "area" (same as row 1), and so on!
Goal: Return all possible word squares you can construct. The same word can be used multiple times, and order doesn't matter.
Input & Output
example_1.py โ Basic Word Square
$Input:["area", "lead", "wall", "lady", "ball"]
โบOutput:[["ball", "area", "lead", "lady"]]
๐ก Note:Only one valid word square can be formed: 'ball', 'area', 'lead', 'lady'. Notice how each row reads the same as its corresponding column.
words[i] consists of only lowercase English letters
All the strings of words are unique
Asked in
Word Squares โ Solution
The optimal approach uses backtracking with Trie optimization. Build a Trie from all words, then use backtracking to construct word squares row by row. For each position, the Trie helps quickly find valid words that match the required prefix from the corresponding column. This dramatically reduces the search space compared to brute force, achieving O(n * k^k) complexity with heavy pruning.
Common Approaches
Approach
Time
Space
Notes
โ
Backtracking with Prefix Matching
O(n * k^k)
O(n * k + k^2)
Use backtracking with early termination based on prefix constraints
Brute Force (Try All Combinations)
O(n^k * k^2)
O(k)
Generate all possible combinations and validate each square
Backtracking with Prefix Matching โ Algorithm Steps
Build a prefix map: group words by their prefixes
Start with an empty square and use backtracking
For each row, determine the required prefix from the current column
Only consider words that match the required prefix
If no valid words exist for a prefix, backtrack immediately
When square is complete, add to results
Visualization
Tap to expand
Step-by-Step Walkthrough
1
Build Prefix Map
Create a mapping from prefixes to words that start with those prefixes
2
Start Backtracking
Begin with first word and recursively add rows
3
Check Constraints
For each new row, ensure it matches column prefix requirements
4
Prune Early
If no word matches required prefix, backtrack immediately
Code -
solution.c โ C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORDS 1000
#define MAX_LEN 10
#define MAX_RESULTS 1000
typedef struct {
char prefix[MAX_LEN];
char words[MAX_WORDS][MAX_LEN];
int count;
} PrefixEntry;
PrefixEntry prefixMap[MAX_WORDS * MAX_LEN];
int prefixCount = 0;
int findPrefixIndex(const char* prefix) {
for (int i = 0; i < prefixCount; i++) {
if (strcmp(prefixMap[i].prefix, prefix) == 0) {
return i;
}
}
return -1;
}
void addToPrefix(const char* prefix, const char* word) {
int index = findPrefixIndex(prefix);
if (index == -1) {
strcpy(prefixMap[prefixCount].prefix, prefix);
strcpy(prefixMap[prefixCount].words[0], word);
prefixMap[prefixCount].count = 1;
prefixCount++;
} else {
strcpy(prefixMap[index].words[prefixMap[index].count], word);
prefixMap[index].count++;
}
}
void buildPrefixMap(char words[][MAX_LEN], int wordCount) {
for (int i = 0; i < wordCount; i++) {
char prefix[MAX_LEN];
for (int j = 0; j <= strlen(words[i]); j++) {
strncpy(prefix, words[i], j);
prefix[j] = '\0';
addToPrefix(prefix, words[i]);
}
}
}
int backtrack(char square[][MAX_LEN], int row, int length,
char results[][10][MAX_LEN], int* resultCount) {
if (row == length) {
for (int i = 0; i < length; i++) {
strcpy(results[*resultCount][i], square[i]);
}
(*resultCount)++;
return 1;
}
char prefix[MAX_LEN];
prefix[0] = '\0';
for (int i = 0; i < row; i++) {
char temp[2] = {square[i][row], '\0'};
strcat(prefix, temp);
}
int prefixIndex = findPrefixIndex(prefix);
if (prefixIndex == -1) return 0;
int found = 0;
for (int i = 0; i < prefixMap[prefixIndex].count; i++) {
char* word = prefixMap[prefixIndex].words[i];
if (strlen(word) == length) {
int valid = 1;
for (int j = row + 1; j < length && valid; j++) {
char colPrefix[MAX_LEN];
colPrefix[0] = '\0';
for (int k = 0; k < row; k++) {
char temp[2] = {square[k][j], '\0'};
strcat(colPrefix, temp);
}
char temp[2] = {word[j], '\0'};
strcat(colPrefix, temp);
if (findPrefixIndex(colPrefix) == -1) {
valid = 0;
}
}
if (valid) {
strcpy(square[row], word);
found += backtrack(square, row + 1, length, results, resultCount);
}
}
}
return found;
}
int wordSquares(char words[][MAX_LEN], int wordCount, char results[][10][MAX_LEN]) {
buildPrefixMap(words, wordCount);
int resultCount = 0;
int wordLengths[MAX_LEN] = {0};
for (int i = 0; i < wordCount; i++) {
wordLengths[strlen(words[i])] = 1;
}
for (int length = 1; length < MAX_LEN; length++) {
if (wordLengths[length]) {
for (int i = 0; i < wordCount; i++) {
if (strlen(words[i]) == length) {
char square[10][MAX_LEN];
strcpy(square[0], words[i]);
backtrack(square, 1, length, results, &resultCount);
}
}
}
}
return resultCount;
}
int main() {
char words[][MAX_LEN] = {"area", "lead", "wall", "lady", "ball"};
int wordCount = 5;
char results[MAX_RESULTS][10][MAX_LEN];
int count = wordSquares(words, wordCount, results);
printf("[");
for (int i = 0; i < count; i++) {
printf("[");
int length = strlen(results[i][0]);
for (int j = 0; j < length; j++) {
printf("\"%s\"", results[i][j]);
if (j < length - 1) printf(",");
}
printf("]");
if (i < count - 1) printf(",");
}
printf("]\n");
return 0;
}
Time & Space Complexity
Time Complexity
โฑ๏ธ
O(n * k^k)
n words, k^k possible squares of size k, but heavy pruning reduces actual complexity
n
2n
โ Linear Growth
Space Complexity
O(n * k + k^2)
Prefix map storage plus recursion stack and current square
n
2n
โก Linearithmic Space
Constraints
1 โค words.length โค 1000
1 โค words[i].length โค 4
All words[i] have the same length
words[i] consists of only lowercase English letters
All the strings of words are unique
25.0K Views
MediumFrequency
~15 minAvg. Time
850 Likes
Ln 1, Col 1
Smart Actions
๐กExplanation
AI Ready
๐ก SuggestionTabto acceptEscto dismiss
// Output will appear here after running code
Code Editor Closed
Click the red button to reopen
Algorithm Visualization
Pinch to zoom โข Tap outside to close
Test Cases
0 passed
0 failed
3 pending
Select Compiler
Choose a programming language
Compiler list would appear here...
AI Editor Features
Header Buttons
๐ก
Explain
Get a detailed explanation of your code. Select specific code or analyze the entire file. Understand algorithms, logic flow, and complexity.
๐ง
Fix
Automatically detect and fix issues in your code. Finds bugs, syntax errors, and common mistakes. Shows you what was fixed.
๐ก
Suggest
Get improvement suggestions for your code. Best practices, performance tips, and code quality recommendations.
๐ฌ
Ask AI
Open an AI chat assistant to ask any coding questions. Have a conversation about your code, get help with debugging, or learn new concepts.
Smart Actions (Slash Commands)
๐ง
/fix Enter
Find and fix issues in your code. Detects common problems and applies automatic fixes.
๐ก
/explain Enter
Get a detailed explanation of what your code does, including time/space complexity analysis.
๐งช
/tests Enter
Automatically generate unit tests for your code. Creates comprehensive test cases.
๐
/docs Enter
Generate documentation for your code. Creates docstrings, JSDoc comments, and type hints.
โก
/optimize Enter
Get performance optimization suggestions. Improve speed and reduce memory usage.
AI Code Completion (Copilot-style)
๐ป
Ghost Text Suggestions
As you type, AI suggests code completions shown in gray text. Works with keywords like def, for, if, etc.
Tabto acceptEscto dismiss
๐ฌ
Comment-to-Code
Write a comment describing what you want, and AI generates the code. Try: # two sum, # binary search, # fibonacci
๐ก
Pro Tip: Select specific code before using Explain, Fix, or Smart Actions to analyze only that portion. Otherwise, the entire file will be analyzed.