资讯详情

资讯详情

轨迹优化算法:前缀和与哈希表应用

1. 题目解析配送轨迹的最优修剪这道题目描述了一个典型的轨迹优化问题给定一个由字符组成的移动指令序列如WASD分别代表上、左、下、右移动我们需要从中删除一段连续的子串使得剩余的指令序列能够将物体从原点(0,0)移动到指定的目标位置(x,y)。如果存在多个可行解我们需要找到删除长度最短的那个。1.1 问题建模首先我们需要明确几个关键要素原始指令序列长度为n的字符串每个字符代表一个移动方向删除区间需要删除的连续子串[start, end]剩余指令原始序列去掉删除区间后的部分目标位置剩余指令执行后应到达的坐标(x,y)举个例子假设原始指令是WASD目标位置是(0,1)删除AS子串第1-2个字符剩余WDW(上)到达(0,1)D(右)到达(1,1) → 不符合删除W子串剩余ASDA(左)到达(-1,0)S(下)到达(-1,-1)D(右)到达(0,-1) → 不符合删除A子串剩余WSDW(上)到达(0,1)S(下)回到(0,0)D(右)到达(1,0) → 不符合删除S子串剩余WADW(上)到达(0,1)A(左)到达(-1,1)D(右)回到(0,1) → 符合条件1.2 核心难点这个问题的挑战在于如何高效地找到满足条件的删除区间。直接暴力枚举所有可能的删除区间O(n^2)时间复杂度在n较大时比如1e5会非常低效。我们需要找到一种线性时间复杂度的解法。2. 算法设计前缀和与哈希表优化2.1 关键观察设原始指令序列的总位移为(totalX, totalY)要删除的子串位移为(delX, delY)则剩余位移应满足 totalX - delX x totalY - delY y因此我们需要找到的子串应该满足 delX totalX - x delY totalY - y2.2 前缀和预处理我们可以预先计算每个位置的前缀位移prefixX[i]前i个指令在x轴上的位移prefixY[i]前i个指令在y轴上的位移这样任意子串s[i..j]的位移可以表示为 delX prefixX[j] - prefixX[i-1] delY prefixY[j] - prefixY[i-1]2.3 哈希表优化查找我们需要找到满足 prefixX[j] - prefixX[i] totalX - x prefixY[j] - prefixY[i] totalY - y可以转换为 prefixX[j] prefixX[i] (totalX - x) prefixY[j] prefixY[i] (totalY - y)因此我们可以预先计算所有位置的(prefixX, prefixY)使用哈希表记录每个(prefixX, prefixY)出现的最早位置对于每个j查找是否存在i使得上述等式成立2.4 算法步骤计算完整序列的总位移(totalX, totalY)计算目标位移差(targetX, targetY) (totalX - x, totalY - y)如果targetX和targetY都为0则无需删除任何子串初始化前缀和数组和哈希表遍历序列维护当前前缀和对于每个位置j查找哈希表中是否存在(prefixX[j] - targetX, prefixY[j] - targetY)如果存在则找到一个候选区间[i1, j]记录满足条件的最小区间长度3. 代码实现与细节处理3.1 Java实现import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { // 示例测试 System.out.println(minDeleteLength(WASD, 0, 1)); // 应返回1 } public static int minDeleteLength(String s, int x, int y) { int n s.length(); int totalX 0, totalY 0; // 计算总位移 for (char c : s.toCharArray()) { switch (c) { case W: totalY; break; case A: totalX--; break; case S: totalY--; break; case D: totalX; break; } } int targetX totalX - x; int targetY totalY - y; // 特殊情况处理 if (targetX 0 targetY 0) { return 0; // 无需删除 } if (n 0) { return -1; // 空序列无法到达非零点 } MapString, Integer map new HashMap(); map.put(0,0, -1); // 初始位置 int minLen Integer.MAX_VALUE; int currX 0, currY 0; for (int j 0; j n; j) { char c s.charAt(j); switch (c) { case W: currY; break; case A: currX--; break; case S: currY--; break; case D: currX; break; } String key (currX - targetX) , (currY - targetY); if (map.containsKey(key)) { int i map.get(key); minLen Math.min(minLen, j - i); } String currKey currX , currY; if (!map.containsKey(currKey)) { map.put(currKey, j); } } return minLen Integer.MAX_VALUE ? -1 : minLen; } }3.2 C实现#include iostream #include unordered_map #include string #include climits using namespace std; int minDeleteLength(const string s, int x, int y) { int n s.size(); int totalX 0, totalY 0; // 计算总位移 for (char c : s) { switch (c) { case W: totalY; break; case A: totalX--; break; case S: totalY--; break; case D: totalX; break; } } int targetX totalX - x; int targetY totalY - y; // 特殊情况处理 if (targetX 0 targetY 0) return 0; if (n 0) return -1; unordered_mapstring, int map; map[0,0] -1; int minLen INT_MAX; int currX 0, currY 0; for (int j 0; j n; j) { switch (s[j]) { case W: currY; break; case A: currX--; break; case S: currY--; break; case D: currX; break; } string key to_string(currX - targetX) , to_string(currY - targetY); if (map.count(key)) { int i map[key]; minLen min(minLen, j - i); } string currKey to_string(currX) , to_string(currY); if (!map.count(currKey)) { map[currKey] j; } } return minLen INT_MAX ? -1 : minLen; } int main() { cout minDeleteLength(WASD, 0, 1) endl; // 应输出1 return 0; }3.3 Python实现def min_delete_length(s: str, x: int, y: int) - int: n len(s) total_x, total_y 0, 0 # 计算总位移 for c in s: if c W: total_y 1 elif c A: total_x - 1 elif c S: total_y - 1 elif c D: total_x 1 target_x, target_y total_x - x, total_y - y # 特殊情况处理 if target_x 0 and target_y 0: return 0 if n 0: return -1 pos_map {} pos_map[(0, 0)] -1 min_len float(inf) curr_x, curr_y 0, 0 for j in range(n): c s[j] if c W: curr_y 1 elif c A: curr_x - 1 elif c S: curr_y - 1 elif c D: curr_x 1 key (curr_x - target_x, curr_y - target_y) if key in pos_map: i pos_map[key] min_len min(min_len, j - i) curr_key (curr_x, curr_y) if curr_key not in pos_map: pos_map[curr_key] j return min_len if min_len ! float(inf) else -1 # 测试示例 print(min_delete_length(WASD, 0, 1)) # 应输出14. 算法分析与优化4.1 时间复杂度计算总位移O(n)构建前缀和并遍历O(n)哈希表操作平均O(1)总体时间复杂度O(n)4.2 空间复杂度哈希表存储前缀和位置最坏情况O(n)总体空间复杂度O(n)4.3 边界情况处理空输入序列直接返回-1无需删除任何子串总位移已等于目标返回0无法通过删除任何子串达到目标返回-1多个可行解时选择最短的删除长度4.4 优化技巧使用更紧凑的哈希键表示可以用(x32)|y的方式将坐标对转换为整数键提前终止当找到长度为1的可行解时可以直接返回内存优化不需要存储完整的前缀和数组只需维护当前前缀和5. 常见问题与调试技巧5.1 典型错误坐标计算错误容易混淆x和y轴的方向确认W/S对应y轴A/D对应x轴确认正方向定义W和D为正A和S为负哈希表初始化遗漏必须初始化(0,0)对应位置-1否则会错过从序列开头删除的情况索引处理错误删除区间长度应为j-i不是j-i1因为i是前缀和的前一个位置5.2 调试建议打印关键变量打印总位移和目标位移差打印遍历过程中的当前前缀和打印哈希表查找结果小规模测试用例测试空序列测试无需删除的情况测试必须删除整个序列的情况测试无法达成目标的情况可视化轨迹对于复杂案例可以绘制原始轨迹和修剪后轨迹确认最终位置是否符合预期5.3 性能优化验证对于n1e5的大规模数据Java/C应能在100ms内完成Python可能需要在500ms内完成如果超时检查是否有不必要的拷贝或低效的哈希操作6. 扩展思考6.1 变种问题允许删除多个不连续的子串问题复杂度将大幅增加可能需要动态规划解法考虑移动代价最小化不同方向移动可能有不同代价需要寻找总代价最小的删除方案三维空间轨迹增加z轴移动指令算法思路可以类似扩展6.2 实际应用场景机器人路径优化删除冗余移动指令优化执行效率游戏AI路径校正修正错误路径指令确保到达目标位置物流配送路线优化调整配送路线避开拥堵路段6.3 算法选择对比暴力法枚举所有可能删除区间O(n^2)时间复杂度适合n较小的情况前缀和哈希表O(n)时间复杂度需要O(n)额外空间适合大规模数据滑动窗口可能适用于某些约束变种但本题不保证单调性难以应用在实际面试中面试官通常会期待候选人能够从暴力法出发逐步优化到线性时间复杂度的解法并清楚地解释每一步优化的思路和原理。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →