HJ17. 坐标移动
题目描述
无限大二维网格上有小人,初始位置为原点 (0, 0)。接收一系列指令进行移动。
指令格式:
- 首字符为
A/S/D/W(左/下/右/上) - 末字符固定为
; - 中间为 1-99 的数字(可含前导零)
非法指令(格式不正确、数字超出范围、方向字符非法等)应被忽略。
输入格式
一行字符串,长度 ≤ 10000,由大写字母、数字和分号组成。保证至少一个 ; 且末尾为 ;。
输出格式
一行两个整数,横纵坐标用逗号间隔。
示例
示例 1
输入:
A10;S20;W10;D30;X;A1A;B10A11;;A10;
输出:
10,-10
说明:
A10:左移 10 →(-10, 0)S20:下移 20 →(-10, -20)W10:上移 10 →(-10, -10)D30:右移 30 →(20, -10)X:非法,忽略A1A:非法,忽略B10A11:非法,忽略- 空指令:忽略
A10:左移 10 →(10, -10)
示例 2
输入:
ABC;AKL;DA1;D001;W023;A100;S00;
输出:
0,0
说明:
A100:数字 100 超出 1-99 范围,非法S00:数字 00 = 0,不在 1-99 范围内,非法
示例 3
输入:
A00;S01;W2;
输出:
0,1
说明:
A00:数字 0 非法S01:数字 1 合法(含前导零),下移 1 →(0, -1)W2:数字 2 合法,上移 2 →(0, 1)
解题思路
第一步:理解问题本质
本题是一个字符串解析 + 模拟题。需要将输入字符串按 ; 分割成多条指令,逐一验证合法性,合法指令则更新坐标。
第二步:分析指令合法性
一条合法指令必须同时满足:
- 非空
- 首字符是
A/S/D/W之一 - 后续部分是 1-2 位数字
- 数字值在 1-99 范围内(含前导零时按实际值计算)
第三步:确定解析方案
使用正则表达式验证指令格式最为简洁:
import re
match = re.fullmatch(r'([ASDW])(\d{1,2})', cmd)
([ASDW]):匹配方向字符(\d{1,2}):匹配 1-2 位数字fullmatch:要求整个字符串完全匹配
再用 int() 转换后验证范围 1 <= distance <= 99。
完整代码实现
import sys
import re
def solve():
line = sys.stdin.readline().strip()
# 方向映射:A=左(x-), D=右(x+), W=上(y+), S=下(y-)
delta = {
'A': (-1, 0),
'D': (1, 0),
'W': (0, 1),
'S': (0, -1),
}
x, y = 0, 0
commands = line.split(';')
for cmd in commands:
if not cmd:
continue
match = re.fullmatch(r'([ASDW])(\d{1,2})', cmd)
if not match:
continue
direction = match.group(1)
distance = int(match.group(2))
if distance < 1 or distance > 99:
continue
dx, dy = delta[direction]
x += dx * distance
y += dy * distance
print(f"{x},{y}")
if __name__ == "__main__":
solve()
示例推演
以输入 A10;S20;W10;D30;X;A1A;B10A11;;A10; 为例:
| 指令 | 方向 | 数字 | 合法性 | 坐标变化 | 当前坐标 |
|---|---|---|---|---|---|
| A10 | A(左) | 10 | ✓ | x -= 10 | (-10, 0) |
| S20 | S(下) | 20 | ✓ | y -= 20 | (-10, -20) |
| W10 | W(上) | 10 | ✓ | y += 10 | (-10, -10) |
| D30 | D(右) | 30 | ✓ | x += 30 | (20, -10) |
| X | - | - | ✗ | 忽略 | (20, -10) |
| A1A | - | - | ✗ | 忽略 | (20, -10) |
| B10A11 | - | - | ✗ | 忽略 | (20, -10) |
| 空 | - | - | ✗ | 忽略 | (20, -10) |
| A10 | A(左) | 10 | ✓ | x -= 10 | (10, -10) |
结果:10,-10
复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间 | O(n) | 遍历所有字符一次,正则匹配为常数时间 |
| 空间 | O(n) | split 产生的指令列表 |
n 为输入字符串长度
易错点总结
1. 范围判断遗漏
# 错误!只判断了格式,没判断数值范围
match = re.fullmatch(r'([ASDW])(\d{1,2})', cmd)
if match: # 但 \d{1,2} 可以匹配 "00" 和 "99",需要进一步验证
解决:提取数字后验证 1 <= distance <= 99。
2. 方向映射错误
# 易混淆的方向映射
delta = {
'A': (-1, 0), # 左:x 减小
'D': (1, 0), # 右:x 增大
'W': (0, 1), # 上:y 增大(注意不是减小!)
'S': (0, -1), # 下:y 减小
}
记忆技巧:W = Up( North,y+),S = Down(South,y-)。
3. 空指令处理
split(';') 会在末尾分号后产生空字符串,必须跳过:
if not cmd:
continue
4. 不用正则的手动解析
如果不想用正则,也可以手动解析,但代码会更长:
def is_valid(cmd):
if len(cmd) < 2:
return False
if cmd[0] not in 'ASDW':
return False
if not cmd[1:].isdigit():
return False
dist = int(cmd[1:])
return 1 <= dist <= 99
扩展思考
- 如果支持负数距离? 如
A-10,需要调整正则为[ASDW]-?\d{1,2}。 - 如果允许多行输入? 外层加
for line in sys.stdin循环。 - 如果指令含小数? 如
A1.5,需要浮点数处理和更复杂的正则。