FIX 风格尾字段校验和:管道分隔消息逐条三态判定
FIX-Style Trailing Checksum Verify: Trinary Label Per Pipe-Delimited Message
开始编码实现 solution(messages: List[str]) -> List[int]。每条消息是一个 ASCII 字符串,按 FIX 风格的线协议组织:tag=value 字段以管道符 | 分隔(这里用 | 替代 FIX 实际的 SOH 0x01 字节,方便 ASCII 文本测试)。协议规定最后一个字段必须是校验和 tag,写作 10=NNN,其中 NNN 是恰好 3 个 ASCII 数字 0-9,零填充表示 0..255 的校验和值。
对每条消息返回 {-1, 0, 1} 中的一个标签:
1:尾字段格式合法10=NNN且int(NNN)等于sum(ord(c) for c in body) % 256,其中body是消息从开头到|10=NNN的|之前那段前缀(若整条消息就是10=NNN,则 body 为空字符串)。0:尾字段在结构上确实是10=NNN,但声明的校验和与重新计算的不一致(这是下游路由的"传输受损告警"通道)。-1:消息结构异常——完全没有10=字段,或者10=不是最后一个字段,或者末三位非全数字,或者10=NNN与前一字段连写没有|分隔。
例如 solution(["8=FIX.4.2|9=12|35=D|34=1|10=041", "55=AAA|11=ORD-1|10=999", "8=FIX.4.2|9=5|35=D"]) 返回 [1, 0, -1]。第一条 body 字节求和模 256 等于 41,声明 041 一致;第二条 body 模 256 等于 200,声明 999 不一致(标 0);第三条压根没有 10= 字段(结构异常,标 -1)。
需要明确的几处细节:单字段消息 "10=000"(整条就是校验和字段)是合法的——body 为空,sum() % 256 == 0,声明 000 一致。"10=001" 则为 0(不一致)。消息体内出现 10= 子串(例如 "10=041|35=D|55=AAA|10=041")是允许的——只有最后字段被解释为校验和 tag,前面的 10= 字节像普通字符那样累加进 body。但 "ABC10=041" 是 -1,因为末尾的 10=041 不是合法 FIX 字段(前一字符是 C 而非 |)。数字之后再有别的字符(如 "...|10=041X")也 -1。三位值里含非数字字符(如 "|10=A12" 或 "|10= 12")同样 -1。
实现禁止使用 re 模块——只能用字符串索引与切片;解析器须在 O(L) 内完成(L 为单条消息长度)。
实现细节由 stubs/stub.py 提供。
例
solution(["8=FIX.4.2|9=12|35=D|34=1|10=041",
"55=AAA|11=ORD-1|10=201",
"8=FIX.4.2|9=5|35=D"])
== [1, 0, -1]第二条 body "55=AAA|11=ORD-1" 实际模 256 为 200,声明 201 偏离 1,标 0;第三条没有 10= 字段,标 -1。
实践背景
一个行情录像/回放工具会从磁盘读入一连串记录下来的 FIX 风格消息(落盘时用 | 替代 SOH 0x01 字节,便于在文本文件中可读),在下游解析器消费之前先逐条贴标签。三态输出(-1 / 0 / 1)让下游路由能做三路决策:结构异常的消息直接丢弃(-1,连字段切分都不可信);声明校验和与重算不符的消息发出告警(0,结构完整但传输中翻位);干净消息直通下游(1)。FIX 的校验和是一种刻意设计成弱保护的"按字节求和模 256"——能抓单 bit 翻转和小段 burst 错误,但抓不住对抗性篡改——它仍然是该协议线层完整性校验的标准做法,所以一个不做这步的回放器会把腐败记录悄无声息地灌进下游模拟器。
约束条件
- 0 <= N <= 1000,其中 N == len(messages);空列表返回空列表
- 0 <= len(messages[i]) <= 4000;允许空字符串
- messages[i] 为 ASCII 字符串,仅含可打印字符(32..126)与分隔符 '|'
- 输出为长度为 N 的整数列表,元素属于 {-1, 0, 1};对结构异常输入不抛异常
- 禁止使用 `re` 模块;只允许字符串索引与切片
样例
Case 1 · statement-example: a valid FIX-flavor message
输入: [["8=FIX.4.2|9=12|35=D|34=1|10=041"]]
期望: [1]
例题输入:消息体校验和与 10= 字段声明一致,输出 1。
Case 2 · typical: valid plus mismatched-cs
输入: [["55=AAA|11=ORD-1|10=200","55=AAA|11=ORD-1|10=201"]]
期望: [1,0]
第二条消息声明的校验和比真实值大 1,标为不通过。
Case 3 · typical: valid + missing 10= + cs-mismatch
输入: [["55=AAA|11=ORD-1|10=200","8=FIX.4.2|9=5|35=D","X=Y|10=243"]]
期望: [1,-1,0]
缺失 10= 字段记为 -1;声明值偏移记为 0。
Case 4 · typical: bare 10=000 has empty body and is valid
输入: [["10=000"]]
期望: [1]
单字段 10=000:空体校验和为 0,声明 000 一致,标为通过。
Case 5 · typical: '10=' substring inside body but real trailer at end
输入: [["8=FIX|10=999|35=D|10=134"]]
期望: [1]
消息体中也含 10=,但解析器只看末尾字段;校验和按整段实际字节求和。
Case 6 · typical: trinary smoke (1, 0, -1)
输入: [["HELLO|10=116","HELLO|10=117","HELLO"]]
期望: [1,0,-1]
同体三种结果:通过/不一致/缺失字段。
Case 7 · typical: consecutive '||' in body still byte-summed correctly
输入: [["A=1|||10=167"]]
期望: [1]
消息体含连续 '||' 也按字节累加;校验和不受字段语义影响。
Case 8 · typical: '10=NNN' literal inside body but real trailer follows
输入: [["10=041|35=D|55=AAA|10=126"]]
期望: [1]
消息体内出现 10=041 文本,但末尾还有真正的 |10=NNN 尾字段,按尾字段判定。
Case 9 · boundary: bare 10=001 has empty body but declared 1 (mismatch)
输入: [["10=001"]]
期望: [0]
空体真实校验和为 0,但声明 001 不一致,记为 0。
Case 10 · boundary: extra char after 10=NNN -> malformed (-1)
输入: [["A=1|10=175X"]]
期望: [-1]
校验和字段后还有 'X',结尾三位不是数字,记为 -1。
Case 11 · boundary: empty input list returns empty list
输入: [[]]
期望: []
空消息列表返回空列表。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: a valid FIX-flavor message
输入: [["8=FIX.4.2|9=12|35=D|34=1|10=041"]]
期望: [1]
例题输入:消息体校验和与 10= 字段声明一致,输出 1。
Case 2 · typical: valid plus mismatched-cs
输入: [["55=AAA|11=ORD-1|10=200","55=AAA|11=ORD-1|10=201"]]
期望: [1,0]
第二条消息声明的校验和比真实值大 1,标为不通过。
Case 3 · typical: valid + missing 10= + cs-mismatch
输入: [["55=AAA|11=ORD-1|10=200","8=FIX.4.2|9=5|35=D","X=Y|10=243"]]
期望: [1,-1,0]
缺失 10= 字段记为 -1;声明值偏移记为 0。
Case 4 · typical: bare 10=000 has empty body and is valid
输入: [["10=000"]]
期望: [1]
单字段 10=000:空体校验和为 0,声明 000 一致,标为通过。
Case 5 · typical: '10=' substring inside body but real trailer at end
输入: [["8=FIX|10=999|35=D|10=134"]]
期望: [1]
消息体中也含 10=,但解析器只看末尾字段;校验和按整段实际字节求和。
Case 6 · typical: trinary smoke (1, 0, -1)
输入: [["HELLO|10=116","HELLO|10=117","HELLO"]]
期望: [1,0,-1]
同体三种结果:通过/不一致/缺失字段。
Case 7 · typical: consecutive '||' in body still byte-summed correctly
输入: [["A=1|||10=167"]]
期望: [1]
消息体含连续 '||' 也按字节累加;校验和不受字段语义影响。
Case 8 · typical: '10=NNN' literal inside body but real trailer follows
输入: [["10=041|35=D|55=AAA|10=126"]]
期望: [1]
消息体内出现 10=041 文本,但末尾还有真正的 |10=NNN 尾字段,按尾字段判定。
Case 9 · boundary: bare 10=001 has empty body but declared 1 (mismatch)
输入: [["10=001"]]
期望: [0]
空体真实校验和为 0,但声明 001 不一致,记为 0。
Case 10 · boundary: extra char after 10=NNN -> malformed (-1)
输入: [["A=1|10=175X"]]
期望: [-1]
校验和字段后还有 'X',结尾三位不是数字,记为 -1。
Case 11 · boundary: empty input list returns empty list
输入: [[]]
期望: []
空消息列表返回空列表。