#include /** * @brief 内部静态辅助:为变长文本流计算初始固定视窗的霍纳法则(Horner's rule)哈希指纹 */ static uint64_t c_RK_ComputeHash(const char* key, c_size_t m, uint64_t R, uint64_t Q) { uint64_t h = 0; for (c_size_t i = 0; i < m; i++) { h = (R * h + (unsigned char)key[i]) % Q; } return h; } /** * @brief 就地初始化 Rabin-Karp 模式串匹配器 (前置计算模式指纹与最高位系数) */ c_err_t c_RabinKarp_Init(c_RabinKarp_t* self, const char* pattern, c_Allocator_t* allocator) { if (!self || !pattern) { return C_ERR_PARAM; } self->allocator = allocator ? *allocator : c_DefaultAllocator; self->m_len = strlen(pattern); if (self->m_len == 0) { return C_ERR_PARAM; } // 1. 为模式串深拷贝开辟独立空间 self->pattern = (char*)c_Allocator_Alloc(&self->allocator, (self->m_len + 1) * sizeof(char)); if (!self->pattern) { return C_ERR_NOMEM; } memcpy(self->pattern, pattern, self->m_len + 1); // 2. 配置商用数论高离散参数 self->R_base = 256; // 扩展 ASCII 字符表基数 self->Q_prime = 1000000007; // 经典防大规模冲突大素数 (10^9 + 7) c_size_t m = self->m_len; uint64_t R = self->R_base; uint64_t Q = self->Q_prime; // 3. 预先推演滚动最高位字符移除时所需要的物理权重乘子:RM = R^(M-1) % Q self->RM = 1; for (c_size_t i = 1; i <= m - 1; i++) { self->RM = (R * self->RM) % Q; } // 4. 计算出当前子串模式串的绝对初始数字指纹 self->pattern_hash = c_RK_ComputeHash(self->pattern, m, R, Q); return C_ERR_OK; } /** * @brief 在主文本 text 中执行 Rabin-Karp 高性能滚动哈希检索(均摊时间复杂度 O(N)) * * @param text 待扫描检索的主文本大字节流(以 '\0' 截止) * @param out_index 找到时,通过二级指针物理填充并回传匹配位置在主文本中的【起始插槽下标】 * @return c_err_t 检索成功返回 C_ERR_OK,未找到匹配项返回 C_ERR_NOTFOUND */ c_err_t c_RabinKarp_Search(const c_RabinKarp_t* self, const char* text, c_size_t* out_index) { if (!self || !text || !out_index) { return C_ERR_PARAM; } c_size_t n = strlen(text); c_size_t m = self->m_len; if (n < m) return C_ERR_NOTFOUND; // 文本总长短于子串,安全拦截 uint64_t R = self->R_base; uint64_t Q = self->Q_prime; uint64_t RM = self->RM; uint64_t pat_hash = self->pattern_hash; // 1. 利用霍纳法则瞬间计算出主文本中 [0, M-1] 初始前缀视窗的哈希值 uint64_t txt_hash = c_RK_ComputeHash(text, m, R, Q); // 2. 拉斯维加斯验证机制:前置哈希完全等价时,启动全貌核验,粉碎极端哈希碰撞 if ((txt_hash == pat_hash) && (strncmp(text, self->pattern, m) == 0)) { *out_index = 0; return C_ERR_OK; } // 3. 🌟🌟🌟【Rabin 指纹极致滚动滑窗内核】🌟🌟🌟 // 游标 i 指向当前滑窗即将【移出】的最高位字符,滑窗单调向右推移 c_size_t limit = n - m; for (c_size_t i = 0; i < limit; i++) { // A. 提取即将移出的高位字符与即将灌入的低位新字符特征 uint64_t char_to_remove = (unsigned char)text[i]; uint64_t char_to_add = (unsigned char)text[i + m]; // B. 滚动变换: // (txt_hash + Q - (char_to_remove * RM) % Q) -> 🌟加 Q 防无符号减法算术下溢回绕! // 随后乘 R 整体左移一格,最后加上低位新字符并整体取模,实现 O(1) 常数级哈希滑窗推演! uint64_t high_remove_part = (char_to_remove * RM) % Q; txt_hash = (txt_hash + Q - high_remove_part) % Q; txt_hash = (txt_hash * R + char_to_add) % Q; // C. 拉斯维加斯联锁判定:计算当前最新滑窗位置处的物理偏移量 c_size_t current_match_offset = i + 1; if (txt_hash == pat_hash) { if (strncmp(text + current_match_offset, self->pattern, m) == 0) { *out_index = current_match_offset; // 精确传出匹配起点物理下标 return C_ERR_OK; } } } return C_ERR_NOTFOUND; } /** * @brief 反初始化:彻底注销并回收 Rabin-Karp 状态机内存空间 */ void c_RabinKarp_Destroy(c_RabinKarp_t* self) { if (self) { if (self->pattern) { c_Allocator_Free(&self->allocator, self->pattern); self->pattern = NULL; } self->m_len = 0; self->pattern_hash = 0; self->RM = 0; } }