3 reference approaches in C, from brute force to the one to reach for. Compare them with yours.
dist(i, j) solves the suffixes that start at word1 + i and word2 + j. An empty suffix costs the length of the other; equal letters cost nothing; otherwise one move plus the cheapest of delete, insert and replace. The cache is one flat malloc'ed block of (n + 1) × (m + 1) ints indexed as i * (m + 1) + j, set to -1 with memset (every byte 0xFF reads back as -1), and freed before returning.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
static int min3(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
static int dist(const char* a, int n, const char* b, int m, int i, int j, int* memo) {
if (i == n) return m - j; // insert the rest of b
if (j == m) return n - i; // delete the rest of a
int* slot = &memo[i * (m + 1) + j];
if (*slot != -1) return *slot;
if (a[i] == b[j]) {
*slot = dist(a, n, b, m, i + 1, j + 1, memo);
} else {
*slot = 1 + min3(dist(a, n, b, m, i + 1, j, memo),
dist(a, n, b, m, i, j + 1, memo),
dist(a, n, b, m, i + 1, j + 1, memo));
}
return *slot;
}
int minDistance(char* word1, char* word2) {
int n = (int)strlen(word1), m = (int)strlen(word2);
size_t cells = (size_t)(n + 1) * (size_t)(m + 1);
int* memo = malloc(cells * sizeof *memo);
memset(memo, -1, cells * sizeof *memo); // all bytes 0xFF = -1 in every int
int result = dist(word1, n, word2, m, 0, 0, memo);
free(memo);
return result;
}
Bottom-up on prefixes: d[i][j] is the distance between the first i letters of word1 and the first j of word2. The table is a C99 variable-length array, int d[n + 1][m + 1], about 362 KB on the stack at the size bound, so there is nothing to free. Row 0 counts inserts, column 0 counts deletes, a match copies the diagonal and a mismatch adds one to the smallest of the cells above, left and above-left.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
static int min3(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
int minDistance(char* word1, char* word2) {
int n = (int)strlen(word1), m = (int)strlen(word2);
int d[n + 1][m + 1]; // a VLA: 301 x 301 ints at most
for (int i = 0; i <= n; i++) d[i][0] = i; // delete i letters
for (int j = 0; j <= m; j++) d[0][j] = j; // insert j letters
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (word1[i - 1] == word2[j - 1]) {
d[i][j] = d[i - 1][j - 1];
} else {
d[i][j] = 1 + min3(d[i - 1][j], d[i][j - 1], d[i - 1][j - 1]);
}
}
}
return d[n][m];
}
Each row reads only the row above, so keep two malloc'ed rows and swap the two pointers after every row; nothing is copied. Swapping the two char* first makes the rows as long as the shorter word, so the memory is O(min(n, m)). Both rows are freed after the answer is read from prev[m].
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
static int min3(int a, int b, int c) {
int m = a < b ? a : b;
return m < c ? m : c;
}
int minDistance(char* word1, char* word2) {
if (strlen(word2) > strlen(word1)) {
char* t = word1; word1 = word2; word2 = t; // rows run over the shorter word
}
int n = (int)strlen(word1), m = (int)strlen(word2);
int* prev = malloc((size_t)(m + 1) * sizeof *prev);
int* cur = malloc((size_t)(m + 1) * sizeof *cur);
for (int j = 0; j <= m; j++) prev[j] = j;
for (int i = 1; i <= n; i++) {
cur[0] = i;
for (int j = 1; j <= m; j++) {
if (word1[i - 1] == word2[j - 1]) {
cur[j] = prev[j - 1];
} else {
cur[j] = 1 + min3(prev[j], cur[j - 1], prev[j - 1]);
}
}
int* t = prev; prev = cur; cur = t;
}
int result = prev[m];
free(prev);
free(cur);
return result;
}