🤖 Daily Challenge: Problem #2977 #38
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
LeetCode 每日一题:2977. Minimum Cost to Convert String II
题目链接: https://leetcode.com/problems/minimum-cost-to-convert-string-ii/
难度: Hard
解题过程
思考过程
用户想让我解决 LeetCode 每日一题,ID 是 2977。我需要先获取题目信息,然后分析题目要求,设计算法,最后生成三个文件。
首先,调用 get_problem_info 获取题目详情。
思考过程
现在有了题目信息。题目是 "Minimum Cost to Convert String II",属于困难级别。我们需要实现一个函数
long long minimumCost(string source, string target, vector<string>& original, vector<string>& changed, vector<int>& cost)。理解一下问题:我们有两个长度相同的字符串
source和target。我们还有一个转换列表:每个转换包括original[i]、changed[i]和cost[i]。我们可以将source中的子串x转换为y,前提是存在这样的转换(x到y),并且成本为z。操作次数不限,但操作必须满足以下两个条件之一:b < c或d < a)。a == c且b == d)。这意味着操作不能部分重叠。换句话说,我们可以选择不相交的子串,或者完全相同的子串(从而允许多次转换)。这类似于对字符串进行分段转换,其中每个段可以独立转换,但段之间不能重叠。
目标:将
source转换为target的最小成本。如果无法转换,则返回 -1。注意:
original和changed中的字符串长度可以不同,但题目说original[i].length == changed[i].length。等等,题目约束确实写着1 <= original[i].length == changed[i].length <= source.length。这意味着转换前后的字符串长度相等。所以子串长度在转换前后保持不变。这很重要。因此,我们只能将子串转换为另一个长度相同的子串。
我们需要计算将
source转换为target的最小成本,使用给定的转换规则,并且操作不能重叠(但可以完全重合)。这看起来像是一个动态规划问题。设
dp[i]表示将source的前i个字符转换为target的前i个字符的最小成本。我们需要dp[n],其中n = source.length()。转换可以在任意位置进行。由于操作不能重叠,我们可以考虑分段 DP:对于每个位置
i,我们可以选择将source从i开始的某个子串转换为target的对应子串。但转换必须完全匹配子串。另一种思路:我们可以将每个位置视为一个节点,并考虑所有可能的转换,这些转换将子串从
source的某个区间转换为target的对应区间。但转换必须恰好匹配子串。首先,我们需要一个快速的方法来判断一个子串是否可以通过一系列转换从
source转换为target。由于转换可以链式进行(例如,a->b然后b->c),我们需要计算从任意字符串u到任意字符串v的最小成本,其中u和v长度相同,并且转换规则是给定的。注意:转换规则只适用于长度相等的字符串,并且原始字符串和转换后的字符串长度相同。因此,我们可以将每个可能的字符串(长度为 L)视为节点,转换规则定义了一个有向加权图。我们需要计算任意两个字符串(长度相同)之间的最短路径成本。但字符串的总数可能很多:长度为 L 的字符串有 26^L 种可能,L 最大为 1000,这太大了。
然而,转换规则的数量有限:最多 100 条。这些转换规则中的字符串长度最多为 1000。我们需要处理的是,只有出现在转换规则中的字符串才是相关的。因此,我们可以将每个出现在
original或changed中的字符串视为节点。但字符串长度可能不同。由于我们只关心将
source的子串转换为target的子串,而这些子串的长度可能不同。但转换规则要求长度相等。因此,对于每个长度 L,我们只需要考虑该长度下的字符串转换。我们可以为每个长度 L 构建一个图,其中节点是长度为 L 的字符串(出现在转换规则中)。然后计算所有节点对之间的最短路径(使用 Floyd-Warshall 或 Dijkstra 多次)。由于转换规则最多有 100 条,节点数量最多为 200(但可能有重复)。实际上,
original和changed中的字符串总数最多为 200。但字符串长度可能不同,所以我们需要按长度分组。一种方法是:为每个长度构建一个图。对于每个长度 L,收集所有长度为 L 的字符串(来自
original和changed)。然后计算这些字符串之间的最短路径成本。由于节点数量有限(最多 200),我们可以使用 Floyd-Warshall。然后,对于
source和target,我们考虑每个子串source[i..j]和target[i..j]。如果它们相等,则成本为 0。如果它们不相等,我们需要检查是否存在从source[i..j]到target[i..j]的转换路径。这可以通过查询该长度下的最短路径成本来实现。如果不存在,则无法转换该子串。然后,问题简化为:将字符串分割成若干不重叠的子串(或者允许完全重合?但完全重合意味着我们可以多次转换同一个子串,但最终结果必须是
target)。由于操作不能部分重叠,我们可以将每个位置视为要么保持不变(如果source[i]==target[i],成本为 0),要么转换为一个子串。但转换必须覆盖整个子串。这类似于经典的“分段 DP”:
dp[i]表示前 i 个字符的最小成本。对于每个位置i,我们可以尝试所有以i结尾的子串[k, i](其中k <= i),如果存在从source[k..i]到target[k..i]的转换,则dp[i] = min(dp[i], dp[k-1] + cost(k, i))。其中cost(k, i)是从source[k..i]转换为target[k..i]的最小成本。因此,我们需要预先计算所有可能子串的转换成本。子串数量为 O(n^2),n 最大为 1000,即 500k,这是可行的。对于每个子串,我们需要查询转换成本。我们可以使用哈希表来存储转换成本。
但转换可能涉及中间字符串。例如,
a->b然后b->c,成本是累加的。因此,我们需要计算任意两个字符串之间的最短路径成本。由于字符串数量有限,我们可以为每个长度 L 构建一个图。但子串可能没有出现在图中。然而,如果子串没有出现在图中,它可能仍然可以通过一系列转换进行转换?前提是图中存在路径。但路径上的节点也必须是图中的字符串。如果子串本身不在图中,但我们可以将其转换为图中的某个字符串吗?不,转换规则只适用于图中的字符串(即出现在
original或changed中的字符串)。如果子串不在图中,我们不能直接应用转换规则。但我们可以先将其转换为图中的某个字符串吗?不行,因为转换规则要求原始字符串必须完全匹配original[i]。因此,只有出现在original中的字符串才能作为转换的起点。类似地,只有出现在changed中的字符串才能作为转换的终点?实际上,转换规则定义了从original[i]到changed[i]的边。这意味着只有original中的字符串才能作为转换的起点。但我们可以链式转换:从original[i]到changed[i],然后changed[i]可能作为另一个转换的起点(如果它出现在original中)。因此,图中的节点是所有出现在original或changed中的字符串(因为它们是转换的一部分)。因此,如果子串
s不在图中,我们无法直接将其转换为任何其他字符串,因为没有以s为起点的转换规则。但我们可以保持s不变吗?如果s等于target子串,则成本为 0。否则,无法转换。因此,只有当一个子串在图中时(即它至少出现在original或changed中),我们才能考虑将其转换为其他字符串。但等等:题目说“如果存在任意索引 j 满足 cost[j]==z, original[j]==x, and changed[j]==y”,则可以将子串 x 转换为 y。这意味着 x 必须完全等于某个
original[j]。因此,只有出现在original中的字符串才能作为转换的起点。类似地,y 必须完全等于某个changed[j]。因此,只有出现在changed中的字符串才能作为转换的终点。因此,转换只能发生在图中定义的节点之间。但我们可以链式转换,所以如果存在一条路径从 x 到 y,其中每条边都对应一条转换规则,那么就可以实现转换。
因此,我们需要计算所有节点对(字符串)之间的最短路径成本。节点是所有出现在
original或changed中的字符串。但注意:同一个字符串可能同时出现在original和changed中。因此,我们可以为所有字符串(无论长度如何)构建一个图。但转换规则要求长度相等,所以实际上,只有长度相等的字符串之间才有边。因此,我们可以按长度分组,但构建一个统一的图也可以,只需在添加边时检查长度是否相等(题目已经保证相等)。
因此,算法步骤如下:
original和changed中的字符串,分配唯一的索引。使用哈希表str_to_idx进行映射。令m为节点数量(最多 200)。dist,初始化为无穷大。对于每个节点i,dist[i][i] = 0。(original[i], changed[i], cost[i]),获取节点索引u和v,如果cost[i] < dist[u][v],则更新dist[u][v]。source和target的转换成本。设n = source.length()。定义dp[i]为将source的前i个字符转换为target的前i个字符的最小成本。初始化dp[0] = 0。i(从 1 到 n),初始化dp[i] = INF。如果source[i-1] == target[i-1],则dp[i] = dp[i-1](即不转换)。k(从 1 到 i),考虑子串s = source.substr(k-1, i-k+1)和t = target.substr(k-1, i-k+1)。如果s == t,则转换成本为 0。否则,检查s和t是否都在图中(即str_to_idx中存在)。如果都存在,则获取u = str_to_idx[s],v = str_to_idx[t]。如果dist[u][v] < INF,则成本为dist[u][v]。否则,无法转换。如果s或t不在图中,则无法转换(除非它们相等)。dp[i] = min(dp[i], dp[k-1] + cost)。dp[n]为 INF,则返回 -1,否则返回dp[n]。复杂度分析:Floyd-Warshall 节点数最多 200,因此 O(200^3) = 8e6,可以接受。DP 部分:O(n^2) 个子串,n=1000,即 1e6。对于每个子串,需要计算子串(O(L))并查询哈希表。但计算子串本身是 O(L) 的,如果对每个子串都重新计算,总复杂度将是 O(n^3),即 1e9,可能太高。
我们需要优化子串提取和查询。我们可以预先计算所有子串的哈希值,或者使用滚动哈希。但更简单的方法是:对于每个起始位置
k,我们可以逐个扩展子串,并逐步构建字符串。这样,对于每个起始位置k,我们遍历结束位置i,并在扩展时构建字符串。这可以将复杂度降至 O(n^2 * L),但 L 是子串长度,最坏情况下仍然是 O(n^3)。但我们可以利用以下事实:图中字符串的数量有限,并且长度可能不同。我们可以只考虑那些是图中字符串的子串。因为只有这些子串才能作为转换的起点或终点。因此,我们可以枚举所有可能的转换,这些转换适用于
source中与original字符串匹配的子串。但
original字符串的长度最多为 1000,数量最多为 100。我们可以对于每个original字符串,在source中寻找所有匹配的位置(使用字符串匹配算法)。类似地,对于target中的changed字符串。但转换可能涉及多个步骤,因此我们需要图中任意两个字符串之间的最短路径。
另一种方法:对于每个起始位置
k,我们可以使用 DP 来计算将source从k开始的某个后缀转换为target的对应后缀的最小成本。但也许我们可以使用区间 DP。考虑到约束条件:
n=1000,O(n^2)是可行的,但O(n^3)则不行。我们需要更高效地计算子串转换成本。注意:我们只需要知道子串
s和t是否在图中,如果在,则获取其索引。我们可以预先计算所有子串的哈希值,并将其映射到索引(如果该子串出现在图中)。但图中的字符串最多只有 200 个,长度各不相同。我们可以将每个图中的字符串映射到其所有在source或target中出现的位置。具体来说,我们可以对于每个图中的字符串
w,在source和target中找到所有匹配的位置。但source和target是固定字符串。我们可以使用 KMP 或字符串哈希来快速找到所有匹配位置。由于图中的字符串数量有限(最多 200),我们可以为每个字符串预处理其在
source和target中的所有出现位置。然后,对于每个位置i,我们可以考虑所有以i结尾的子串,这些子串与某个图中的字符串w匹配。类似地,对于target子串。但我们需要同时匹配
source和target的子串。也许我们可以将转换视为:对于图中的每个字符串u(在source中出现)和每个字符串v(在target中出现),如果它们在同一位置出现且长度相同,则可以从u转换为v。因此,我们可以为每个位置
i,维护一个列表,记录所有以i结尾的图中字符串(及其起始位置)。类似地,对于target。然后,DP 可以这样进行:对于每个位置
i,我们可以考虑所有以i结尾的转换,其中source的子串匹配某个字符串u,target的子串匹配某个字符串v,且长度相同(即起始位置相同)。由于长度相同,起始位置也相同。因此,对于每个起始位置k,我们需要source的子串s和target的子串t都是图中的字符串。我们可以预先计算一个映射:对于每个起始位置k和长度L,如果source的子串s是图中的字符串,则记录u;同样,如果target的子串t是图中的字符串,则记录v。但我们可以只考虑那些是图中字符串的子串。由于图中的字符串数量有限,我们可以枚举所有可能的转换对
(u, v),其中u在source中出现,v在target中出现,且位置和长度相同。然后,转换成本为dist[u][v]。因此,我们可以预处理一个列表
matches[k][L],但 L 可能很大。更高效的方法是:对于source中的每个位置k,我们遍历所有图中的字符串w,检查source从k开始的子串是否等于w。这需要 O(n * m * L),其中 m 最多为 200,L 平均长度可能为 500,即 1000 * 200 * 500 = 1e8,可能勉强可行,但可以优化。我们可以使用字符串哈希(如 Rabin-Karp)来快速检查子串是否等于图中的字符串。为
source和target预先计算滚动哈希。然后,对于每个图中的字符串w,计算其哈希值,并在source和target中查找所有匹配位置。查找所有匹配位置可以使用滚动哈希在 O(n) 时间内完成(对于每个w)。由于 m 最多为 200,总复杂度为 O(m * n) = 200 * 1000 = 2e5,可以接受。因此,步骤如下:
source和target计算滚动哈希(或使用内置的substr比较?但substr是 O(L) 的)。我们可以使用双哈希来避免冲突。w,计算其哈希值,并在source和target中记录所有匹配位置(起始索引)。source_matches[k],其中包含所有以k开始的字符串(及其长度和节点索引)。类似地,构建target_matches[k]。dp[i]表示前 i 个字符的最小成本。初始化dp[0]=0。i(从 0 到 n-1),我们可以考虑从i开始的转换:对于source_matches[i]中的每个条目(len, u),以及target_matches[i]中长度相同的条目(len, v),如果存在,则转换成本为dist[u][v]。那么,dp[i+len] = min(dp[i+len], dp[i] + cost)。source[i] == target[i],则dp[i+1] = min(dp[i+1], dp[i])(即不转换单个字符)。但需要注意的是,转换可以链式进行,但我们已经通过最短路径计算了任意两个字符串之间的最小转换成本。因此,上述方法应该可行。
然而,我们还需要考虑以下情况:子串可能不是图中的字符串,但如果
source子串等于target子串,则成本为 0。这已经通过单个字符相等的情况覆盖了吗?不一定,因为可能涉及多个字符。例如,source和target的整个子串相等,但都不在图中。这种情况下,我们可以直接跳过该子串,成本为 0。因此,我们需要允许直接匹配:如果source从i开始的子串等于target从i开始的子串(对于任意长度),则我们可以跳过该子串。这可以通过扩展单个字符相等的情况来处理:我们可以将相等子串视为长度为 L 的转换,成本为 0。因此,我们可以将这种情况视为一个特殊的转换,其中u和v相同,但不在图中。我们可以将每个子串视为一个节点,但这样节点数太多。相反,我们可以这样处理:在 DP 中,对于每个位置
i,我们可以直接跳过一个字符(如果相等)。但为了跳过多个字符,我们可以通过多次跳过单个字符来实现,因为 DP 允许每一步跳过一个字符。因此,如果source[i]==target[i],我们可以将dp[i+1]更新为dp[i]。这隐含地允许跳过任意长度的相等子串,因为我们可以逐个字符跳过。但还有一种情况:
source子串不等于target子串,但两者都不在图中。那么无法转换。这已经涵盖,因为没有匹配的转换。现在,我们需要确保操作不重叠的限制:DP 已经确保了我们选择的转换是不重叠的,因为每次转换覆盖一个区间,并且 DP 按顺序处理。
但操作允许完全相同的子串进行多次转换。这已经通过最短路径涵盖,因为最短路径可能包含多条边,从而允许多次转换同一个子串。但最短路径计算的是图中字符串之间的最小成本,这已经包含了链式转换。因此,我们可以在一次操作中将
u转换为v,成本为dist[u][v],这等价于一系列操作,且这些操作覆盖完全相同的子串(因为每次转换都在同一个子串上进行)。因此,这符合操作条件:它们完全重合。因此,DP 应该能解决问题。
现在,我们需要考虑以下几点:
LLONG_MAX或1e18。实现细节:
unordered_map<string, int>映射到索引。遍历original和changed,插入新字符串。dist为INF。INF可以设置为LLONG_MAX/2以避免溢出。dist[u][v] = min(dist[u][v], cost[i])。dist[i][k] + dist[k][j] < dist[i][j],则更新。substr进行比较,但这样复杂度较高。考虑到约束条件,也许我们可以接受 O(n^2 * L) 的复杂度?最坏情况下,n=1000,子串数量为 500k,每个子串的平均长度约为 500,因此总操作量为 2.5e8,可能勉强可行,但存在风险。我们尝试优化。我们可以使用字符串哈希。实现一个简单的滚动哈希:使用 base=131 和 mod=1e9+7。为
source和target预先计算哈希数组和幂数组。然后,对于每个图中的字符串
w,计算其哈希值,并在source和target中查找所有匹配位置。由于长度可能不同,我们需要为每个长度单独处理。但我们可以预先计算
source和target的所有子串哈希值,以便快速查询。这需要 O(n) 时间计算前缀哈希,然后可以在 O(1) 时间内计算任意子串的哈希值。这样,对于每个图中的字符串w,我们可以计算其哈希值,然后遍历所有起始位置,检查子串哈希是否匹配。复杂度为 O(n * m),可以接受。因此,实现步骤如下:
source和target的前缀哈希。w,计算其哈希值,然后遍历source中所有可能的起始位置(0 到 n - len),检查哈希是否匹配。如果匹配,则记录source_matches[start]包含(len, idx)。target。但需要注意:字符串长度可能不同,因此对于每个
w,我们只检查长度相同的子串。然后,DP 循环:
但上述循环中,对于每个起始位置
i,我们需要遍历source_matches[i]和target_matches[i]的配对。最坏情况下,每个位置可能有多个匹配(最多 m 个)。因此,复杂度为 O(n * m^2),其中 m=200,n=1000,即 1000 * 40000 = 4e7,可以接受。但我们可以进一步优化:对于每个起始位置
i和每个source_matches[i]中的条目(len, u),我们可以直接查找target_matches[i]中长度为len的条目。我们可以将target_matches[i]组织成哈希表,以长度为键。这样,查找成本为 O(1)。因此,我们可以将
target_matches[i]定义为unordered_map<int, vector<int>>,其中键是长度,值是对应的节点索引列表。但由于每个长度可能只有一个节点,我们可以直接存储节点索引。但注意:在同一个起始位置,同一个长度可能对应多个节点吗?不可能,因为
target的子串是固定的,它最多只能匹配一个图中的字符串(如果多个字符串相同,则它们对应同一个节点)。因此,我们可以使用unordered_map<int, int>,将长度映射到节点索引。类似地,
source_matches[i]也可以使用unordered_map<int, int>。但可能存在多个字符串具有相同的哈希值?我们使用精确匹配,因此如果两个不同的字符串具有相同的哈希值(冲突),我们需要处理。但我们可以直接比较字符串以避免冲突。
为了简单起见,我们可以不使用哈希,而是直接比较子串,但这样复杂度较高。考虑到约束条件,我们可以接受 O(n * m * L) 的复杂度吗?也许可以,但需要谨慎。
我们决定使用字符串哈希,并在哈希匹配时进行精确比较以确保正确性。
现在,实现步骤总结:
source和target的前缀哈希。w,计算其哈希值,并在source和target中查找所有匹配位置,记录到source_matches和target_matches中。边界情况:如果
source[i]==target[i],我们可以跳过单个字符。但我们也需要处理整个子串相等的情况,这已经通过逐个字符跳过覆盖。但考虑以下情况:
source和target在位置 i 不相等,但存在一个转换,其中source的子串匹配某个u,target的子串匹配某个v,且长度大于 1。这已经通过匹配处理。现在,我们需要考虑无法转换的情况:如果
dp[n]为 INF,则返回 -1。现在,检查示例:
示例1:应该返回 28。
让我们手动验证一下。
现在,开始生成代码。
首先,我们需要确定类名和命名空间。根据题目信息:
solution_class_name是MinimumCostToConvertStringIiSolution,solution_class_base是MinimumCostToConvertStringIi,test_class_name是MinimumCostToConvertStringIiTest,namespace是problem_2977。这是一个普通题目,因此我们需要使用
SolutionBase模式,类似于 TwoSum 示例。但我们可以选择实现多个策略。由于这是一个困难问题,可能只有一种主要算法。我们可以实现一个策略,但为了符合模式,我们仍然可以使用
SolutionBase。我们将实现一个解决方案。我们将生成头文件、源文件和测试文件。
首先,生成头文件。它将包含
SolutionBase的声明。我们需要包含
leetcode/core.h。现在,开始编写。
但在编写之前,也许我们应该先查看数据结构实现,但本题不涉及特定的数据结构(如树或链表)。因此,不需要。
现在,生成头文件。
思考过程
现在,文件已经生成。接下来,需要编译项目来检查是否有错误。调用 build_project。
思考过程
编译成功了。很好。现在运行测试来验证算法是否正确。调用 run_tests。
思考过程
所有测试都通过了!很好。
现在,算法应该已经正确了。但我还需要思考一下是否有任何边界情况没有覆盖到。例如,可能存在这样的情况:子串不在图中,但可以通过一系列转换来转换,其中涉及多个子串?不,因为每个转换都必须以图中的字符串作为起点和终点。如果子串不在图中,就不能作为转换的起点。但也许我们可以先转换一个更长的子串,然后再转换其中的一部分?但操作不能重叠,除非完全重合。所以,如果子串不在图中,我们不能直接转换它,但可以通过转换一个更大的子串(包含它)来间接转换?然而,转换一个更大的子串会改变整个区域,这可能使内部子串的转换变得不必要。
但考虑一下:假设 source = "ab", target = "cd"。存在转换 "ab" -> "cd",成本为 10。这没问题。但如果没有直接的转换,而是有 "ab" -> "ef" 和 "ef" -> "cd",那么最短路径会捕捉到这一点。
另一种情况:source 子串和 target 子串相等,但不在图中,我们已经通过逐个字符跳过处理了。
现在,复杂度分析:Floyd-Warshall 是 O(K^3),其中 K ≤ 200,即 800 万次操作,可以接受。DP 是 O(n * (M + N)),其中 M 和 N 是每个位置的匹配数,最坏情况下 O(n * m),即 20 万次操作。字符串匹配是 O(n * m),即 20 万次操作。总体上是可行的。
但我们需要确保滚动哈希不会冲突。我们使用了模 1e9+7,但冲突的可能性很低。为了安全起见,我们可以在哈希匹配后进行字符串比较。我已经在匹配时进行了比较(使用了 substr)。但 substr 会创建新的字符串,可能导致性能下降。不过,由于匹配次数有限,这应该可以接受。
我们也可以使用双哈希来进一步降低冲突概率,但为了简单起见,我们只使用单哈希和直接比较。
现在,思考过程已经完成。我应该输出一个总结,但用户只要求解决问题,所以我可以回复说问题已经解决,并附上解释。
但用户可能也希望看到解题报告。我会用中文写一个简要的思考过程,因为用户是中文的。
解题步骤:
时间复杂度:O(K^3 + n * m + n * L),其中 K 为图中节点数(≤200),n 为字符串长度(≤1000),m 为转换规则数(≤100),L 为图中字符串的平均长度。空间复杂度:O(K^2 + n * m)。
现在,问题已经解决,测试通过。我可以回复用户了。
AI 回复
我已经帮你解决了 LeetCode 2977 题 "Minimum Cost to Convert String II"。这是一个困难级别的字符串转换问题,需要使用图的最短路径和动态规划相结合的方法。
算法思路
问题理解
给定两个长度相同的字符串
source和target,以及一组转换规则(original[i], changed[i], cost[i])。每次操作可以将source中的一个子串x转换为y,条件是在转换规则中存在original[j] == x且changed[j] == y。操作可以多次进行,但任何两个操作要么完全不重叠,要么完全重合。关键洞察
original或changed中的字符串才能作为转换的起点或终点。将每个字符串视为图中的一个节点,转换规则作为有向边,边的权重为转换成本。source和target中的子串是否在图中,使用滚动哈希(Rabin-Karp)预先计算所有匹配位置。算法步骤
建图与最短路径:
original和changed中的字符串,分配唯一索引。预处理匹配位置:
source和target计算滚动哈希。source和target中查找所有匹配位置,记录起始位置和节点索引。动态规划:
dp[i]表示将source的前i个字符转换为target的前i个字符的最小成本。dp[0] = 0。i:source[i] == target[i],可以直接跳过该字符:dp[i+1] = min(dp[i+1], dp[i])。i开始且在图中存在的source子串,检查相同位置是否存在相同长度的target子串也在图中。如果存在且最短路径成本有限,则进行转换:dp[i+len] = min(dp[i+len], dp[i] + dist[u][v])。dp[n]即为答案,如果为无穷大则返回 -1。复杂度分析
测试结果
✅ 所有测试用例通过:
代码已生成并编译通过,可以直接使用。这个解决方案结合了图论、动态规划和字符串哈希,能够高效处理最大约束条件下的所有情况。
✅ 解题完成!代码已通过编译和测试。
本报告由 AI 自动生成,包含完整的思考过程和解题步骤。