91 lines
3.1 KiB
C
91 lines
3.1 KiB
C
#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;
|
||
|
|
}
|