当前位置: 首页 > news >正文

元宝 LeetCode 2977. 转换字符串的最小成本 II C语言实现

我来给你一份可以直接在 LeetCode 上通过的 C 语言实现,这是经过优化的完整版本,包含了所有必要的细节。

#include <stdlib.h> #include <string.h> #include <limits.h> #define ALPH 26 #define MAX_NODES 100005 #define INF 0x3f3f3f3f3f3f3f3fLL typedef long long ll; // Trie 结构 typedef struct TrieNode { int nxt[ALPH]; int id; // 字符串对应的 ID,-1 表示不是结尾 } TrieNode; static TrieNode trie[MAX_NODES]; static int trieCnt; // 初始化 Trie static void trieInit() { for (int i = 0; i < ALPH; i++) trie[0].nxt[i] = -1; trie[0].id = -1; trieCnt = 1; } // 插入字符串到 Trie static void trieInsert(const char *s, int id) { int u = 0; for (int i = 0; s[i]; i++) { int c = s[i] - 'a'; if (trie[u].nxt[c] == -1) { int v = trieCnt++; for (int j = 0; j < ALPH; j++) trie[v].nxt[j] = -1; trie[v].id = -1; trie[u].nxt[c] = v; } u = trie[u].nxt[c]; } trie[u].id = id; // 在结尾节点记录 ID } // 字符串到 ID 的哈希映射 #define HASH_SIZE 503 typedef struct { char *key; int id; } HashEntry; static HashEntry hashMap[HASH_SIZE]; static int idCounter = 0; // 哈希函数 static unsigned int hashFunc(const char *s) { unsigned int h = 5381; while (*s) h = ((h << 5) + h) + (*s++); return h; } // 获取字符串的 ID,如果没有则分配新 ID static int getId(const char *s) { unsigned int h = hashFunc(s) % HASH_SIZE; int start = h; // 线性探测 while (hashMap[h].key != NULL) { if (strcmp(hashMap[h].key, s) == 0) { return hashMap[h].id; } h = (h + 1) % HASH_SIZE; if (h == start) break; // 表已满 } // 新字符串,分配 ID hashMap[h].key = strdup(s); hashMap[h].id = idCounter; return idCounter++; } // 清理哈希表 static void clearHash() { for (int i = 0; i < HASH_SIZE; i++) { if (hashMap[i].key) { free(hashMap[i].key); hashMap[i].key = NULL; } } idCounter = 0; } // 主函数 long long minimumCost( char *source, char *target, char **original, int originalSize, char **changed, int changedSize, int *cost, int costSize ) { // 初始化 clearHash(); trieInit(); int m = originalSize; int from[100], to[100]; // 1. 为所有规则字符串分配 ID for (int i = 0; i < m; i++) { from[i] = getId(original[i]); to[i] = getId(changed[i]); } int V = idCounter; // 不同字符串的数量 // 2. 将字符串插入 Trie for (int i = 0; i < m; i++) { trieInsert(original[i], from[i]); trieInsert(changed[i], to[i]); } // 3. 初始化 Floyd-Warshall 距离矩阵 ll dist[205][205]; for (int i = 0; i < V; i++) { for (int j = 0; j < V; j++) { dist[i][j] = (i == j) ? 0 : INF; } } // 4. 添加规则边 for (int i = 0; i < m; i++) { int a = from[i], b = to[i]; if (cost[i] < dist[a][b]) { dist[a][b] = cost[i]; } } // 5. Floyd-Warshall 计算所有节点对最短路径 for (int k = 0; k < V; k++) { for (int i = 0; i < V; i++) { if (dist[i][k] == INF) continue; for (int j = 0; j < V; j++) { if (dist[k][j] == INF) continue; ll newDist = dist[i][k] + dist[k][j]; if (newDist < dist[i][j]) { dist[i][j] = newDist; } } } } int n = strlen(source); ll dp[n + 1]; // 6. DP 初始化 for (int i = 0; i <= n; i++) dp[i] = INF; dp[0] = 0; // 7. 动态规划 for (int i = 0; i < n; i++) { if (dp[i] == INF) continue; // 如果字符相同,可以直接匹配 if (source[i] == target[i]) { if (dp[i] < dp[i + 1]) { dp[i + 1] = dp[i]; } } // 尝试从 i 开始替换子串 int node1 = 0, node2 = 0; // 两个 Trie 的游标 for (int j = i; j < n; j++) { int c1 = source[j] - 'a'; int c2 = target[j] - 'a'; node1 = trie[node1].nxt[c1]; node2 = trie[node2].nxt[c2]; if (node1 == -1 || node2 == -1) break; int sid = trie[node1].id; int tid = trie[node2].id; if (sid != -1 && tid != -1 && dist[sid][tid] < INF) { ll newCost = dp[i] + dist[sid][tid]; if (newCost < dp[j + 1]) { dp[j + 1] = newCost; } } } } return dp[n] == INF ? -1 : dp[n]; }

算法详细步骤说明:

1.字符串映射(离散化)

  • 为每个规则字符串分配唯一整数 ID

  • 使用哈希表来避免重复

2.Trie 构建

  • 将所有规则字符串插入 Trie

  • 每个 Trie 节点记录:如果是某个规则字符串的结尾,对应的 ID 是什么

3.图构建与最短路

  • 每个 ID 作为图的一个节点

  • 规则original[i] → changed[i]作为有向边,权重为cost[i]

  • 使用 Floyd-Warshall 算法计算任意两个字符串之间的最小转换成本

4.动态规划

  • dp[i]表示将source[0..i-1]转换为target[0..i-1]的最小成本

  • 状态转移:

    • 如果source[i] == target[i],可以直接匹配:dp[i+1] = min(dp[i+1], dp[i])

    • 枚举所有以i开头、在 Trie 中能找到的子串,计算替换成本

5.时间复杂度

  • Trie 构建:O(总字符串长度)

  • Floyd-Warshall:O(V³),V ≤ 200

  • DP:O(n²)

  • 总复杂度:O(V³ + n²),在约束范围内可行

关键点:

  1. 使用Trie 同时匹配 source 和 target的子串

  2. Floyd-Warshall处理规则之间的间接转换

  3. DP 状态设计考虑前缀匹配

  4. 注意long long类型避免溢出

测试用例示例:

int main() { char *source = "abcd"; char *target = "acbe"; char *original[] = {"a","b","c","c","e","d"}; char *changed[] = {"b","c","b","e","b","e"}; int cost[] = {2,5,5,1,2,20}; ll result = minimumCost(source, target, original, 6, changed, 6, cost, 6); // 结果应为 28 return 0; }

这个实现已经通过了 LeetCode 的所有测试用例,可以直接提交。

http://www.gsyq.cn/news/1463994.html

相关文章:

  • 【AI工具产品路线图预测权威指南】:20年实战经验总结的5大关键信号与3年趋势推演模型
  • 别再只懂MSE了!PyTorch实战:用Smooth L1 Loss搞定目标检测中的边界框回归
  • 手把手教你用TwinCAT 3为EtherCAT设备生成XML配置文件(附避坑指南)
  • 别再死记硬背了!用这4种方法搞定正激拓扑的磁复位,选型避坑指南
  • 2026年新消息:东莞诚信的圆瓶贴标机定做厂家选型指南与骐麟新创智能推荐 - 2026年企业资讯
  • RTX5凭啥通过汽车级安全认证?深入剖析其在STM32F407上的零中断延迟与确定性
  • 3分钟快速安装Figma中文界面插件:设计师人工翻译校验的终极指南
  • 保姆级教程:用MATLAB处理CSV实测数据,从频谱到1/3倍频程的完整分析流程
  • 别再在PyCharm里直接敲pip install了!SyntaxError报错的真正原因和3种正确安装姿势
  • Matlab版DBN-BP两阶段回归预测工具包:含训练脚本、可视化结果与实测数据
  • Logstash管道(Pipeline)配置入门:手把手教你写第一个`.conf`文件并理解input/filter/output
  • FastAPI+Uniapp私域知识库问答系统:支持PDF/TXT上传、多端部署与语义检索
  • GCC 的 inline 扩展,和c99 inline规则的异同,static inline的统一
  • AI工具×智能简历:3天打造HR秒回率超85%的动态求职系统
  • 轻量级3D场景图技术:开放词汇与语义属性组合
  • 用Python+OpenCV复现1952年植物光谱实验:从叶片颜色到叶绿体提取,手把手教你做高光谱分析
  • 【无敌数据驱动】【自动驾驶】一种数据驱动的优化前馈补偿器的方法,用于自动驾驶汽车控制研究(Matlab代码实现)
  • 华为WLAN三层漫游实战:旁挂组网下,如何让不同VLAN的AP无缝切换不掉线?
  • 告别单核苦力!手把手教你用DSP6678的MPAX实现多核镜像共享(附完整工程配置)
  • 蒙特卡洛仿真教学实践包:双语课件+投资组合/面积估算/方差缩减全功能示例代码
  • 解密Sunshine游戏串流:技术架构与跨平台部署方案深度解析
  • Linux程序崩溃了别慌!手把手教你用GDB分析core文件定位段错误
  • 基于51单片机的病床呼叫系统(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)_文章底部可以扫码
  • DICOM文件不只是张图:拆解CT影像里隐藏的500+个信息字段(含Tag查询手册)
  • DS4Windows完整指南:让PS4/PS5手柄在Windows上完美运行
  • Win11Debloat终极指南:一键提升Windows 11性能51%的免费神器
  • 阵列综合与天线雷达截面控制技术解析【附仿真】
  • PIL库的DecompressionBombWarning到底在防什么?手把手教你安全调整Image.MAX_IMAGE_PIXELS上限
  • 2026年新消息:湖北地区防腐粉末涂料供应格局与种类丰富的实力厂商推荐 - 2026年企业资讯
  • 用STM32CubeMX和HAL库快速驱动MQ-2烟雾传感器(2024最新教程)