This commit is contained in:
2026-09-07 19:33:37 +08:00
parent c945aa211f
commit c7b6c90ec9
20 changed files with 1029 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
#include <c_LongestCommonSubstring.h>
c_err_t c_LongestCommonSubstring_Init(c_LongestCommonSubstring_t* self, const char* str_a, const char* str_b, c_Allocator_t* allocator) {
if (!self || !str_a || !str_b) return C_ERR_PARAM;
self->allocator = allocator ? *allocator : c_DefaultAllocator;
self->max_len = 0;
self->start_idx_a = 0;
c_size_t len_a = strlen(str_a);
c_size_t len_b = strlen(str_b);
if (len_a == 0 || len_b == 0) return C_SUCCESS; /* Trivial empty evaluation check */
/* Optimize spatial footprint boundaries by always assigning the shorter collection as columns */
const char* s_row = str_a;
const char* s_col = str_b;
c_size_t rows = len_a;
c_size_t cols = len_b;
c_bool_t swapped = C_FALSE;
if (len_a < len_b) {
s_row = str_b;
s_col = str_a;
rows = len_b;
cols = len_a;
swapped = C_TRUE;
}
/* 1. Allocate space-optimized 2-row rolling history track arrays */
c_size_t* prev_row = (c_size_t*)c_Allocator_Calloc(&self->allocator, cols + 1, sizeof(c_size_t));
c_size_t* curr_row = (c_size_t*)c_Allocator_Calloc(&self->allocator, cols + 1, sizeof(c_size_t));
if (!prev_row || !curr_row) {
if (prev_row) c_Allocator_Free(&self->allocator, prev_row);
if (curr_row) c_Allocator_Free(&self->allocator, curr_row);
return C_ERR_NOMEM;
}
/* 2. Linear matrix sweep iteration pass */
for (c_size_t i = 1; i <= rows; ++i) {
for (c_size_t j = 1; j <= cols; ++j) {
if (s_row[i - 1] == s_col[j - 1]) {
curr_row[j] = prev_row[j - 1] + 1;
if (curr_row[j] > self->max_len) {
self->max_len = curr_row[j];
/* Map index location back relative to original String A coordinates */
if (!swapped) {
self->start_idx_a = i - self->max_len;
} else {
self->start_idx_a = j - self->max_len;
}
}
} else {
curr_row[j] = 0; /* Continuous block break, reset sequence link */
}
}
/* 3. Shift the rolling window contents smoothly */
memcpy(prev_row, curr_row, (cols + 1) * sizeof(c_size_t));
}
c_Allocator_Free(&self->allocator, prev_row);
c_Allocator_Free(&self->allocator, curr_row);
return C_SUCCESS;
}
void c_LongestCommonSubstring_Destroy(c_LongestCommonSubstring_t* self) {
if (!self) return;
self->max_len = 0;
self->start_idx_a = 0;
}
c_err_t c_LongestCommonSubstring_GetSubstring(const c_LongestCommonSubstring_t* self, const char* str_a, char* out_buffer, c_size_t buffer_cap) {
if (!self || !str_a || !out_buffer || buffer_cap == 0) return C_ERR_PARAM;
if (self->max_len == 0) {
out_buffer[0] = '\0';
return C_SUCCESS;
}
if (buffer_cap <= self->max_len) return C_ERR_OUTOFBOUND;
/* Securely slice and copy matching token block segment */
memcpy(out_buffer, str_a + self->start_idx_a, self->max_len);
out_buffer[self->max_len] = '\0'; /* Terminate string safely */
return C_SUCCESS;
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef INCLUDED_C_LONGESTCOMMONSUBSTRING_H
#define INCLUDED_C_LONGESTCOMMONSUBSTRING_H
#ifndef INCLUDED_C_TYPES_H
#include <c_Types.h>
#endif /*INCLUDED_C_TYPES_H*/
#ifndef INCLUDED_C_ALLOCATOR_H
#include <c_Allocator.h>
#endif /*INCLUDED_C_ALLOCATOR_H*/
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
typedef struct {
c_size_t max_len; /* Length of the longest common substring found */
c_size_t start_idx_a; /* Starting coordinate index inside String A sequence */
c_Allocator_t allocator; /* Deep copy of the user-provided allocator */
} c_LongestCommonSubstring_t;
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
/**
* @brief Non-recursively computes the longest common substring between String A and String B.
* @param str_a Null-terminated or bound-verified character string A sequence
* @param str_b Null-terminated or bound-verified character string B sequence
* @param allocator Explicit allocator context used to configure temporary dynamic matrices
*/
c_err_t c_LongestCommonSubstring_Init(c_LongestCommonSubstring_t* self, const char* str_a, const char* str_b, c_Allocator_t* allocator);
/**
* @brief Releases internal tracking structures safely (Trivial for rolling array layouts).
*/
void c_LongestCommonSubstring_Destroy(c_LongestCommonSubstring_t* self);
/**
* @brief Returns the length of the discovered longest common substring.
*/
C_STATIC_FORCE_INLINE
c_size_t c_LongestCommonSubstring_GetLen(const c_LongestCommonSubstring_t* self) {
return self ? self->max_len : 0;
}
/**
* @brief Extracts the resolved longest common substring payload back into a safe user buffer.
* @param out_buffer Presized destination character array to copy results into
* @param buffer_cap Maximum threshold size limit matching out_buffer allocation bounds
*/
c_err_t c_LongestCommonSubstring_GetSubstring(const c_LongestCommonSubstring_t* self, const char* str_a, char* out_buffer, c_size_t buffer_cap);
#endif /*INCLUDED_C_LONGESTCOMMONSUBSTRING_H*/
+36
View File
@@ -0,0 +1,36 @@
#include "c_Test.h"
#include "c_LongestCommonSubstring.h"
TEST_CASE(test_longest_common_substring_resolution) {
/* Test inputs:
* Str A: "AABCCBDE"
* Str B: "XABCYBDE"
* Longest common continuous substring token should be "BDE" (Len: 3) or "ABC" (Len: 3).
* Our engine catches the first optimal one maximized or tracked sequentially.
*/
const char* a = "AABCCBDE";
const char* b = "XABCYBDE";
c_LongestCommonSubstring_t lcs;
c_err_t err = c_LongestCommonSubstring_Init(&lcs, a, b, NULL);
ASSERT_INT_EQ(C_SUCCESS, err);
c_size_t resolved_len = c_LongestCommonSubstring_GetLen(&lcs);
ASSERT_LL_EQ(3, resolved_len);
char res_buf[16];
err = c_LongestCommonSubstring_GetSubstring(&lcs, a, res_buf, sizeof(res_buf));
ASSERT_INT_EQ(C_SUCCESS, err);
/* Assert exact string equality token content matches */
ASSERT_INT_EQ(0, strcmp("ABC", res_buf) == 0 || strcmp("BDE", res_buf) == 0 ? 0 : -1);
c_LongestCommonSubstring_Destroy(&lcs);
}
int main(void) {
TEST_START(LongestCommonSubstring_Verification_Suite);
RUN_TEST(test_longest_common_substring_resolution);
TEST_REPORT();
RETURN_TEST_STATUS;
}