Codeforces 39J提交失败求助:样例通过但评测不通过,逻辑错误排查
Codeforces 39J提交失败求助:样例通过但评测不通过,逻辑错误排查
看起来你遇到了典型的“样例过了但评测挂了”的坑,这种情况通常是代码逻辑没覆盖到边界case,或者有致命的输入处理错误,咱们一步步来捋你的问题:
你的问题背景回顾
给定两个小写单词,其中一个恰好比另一个长1个字符。需要判断:从长单词中删除一个字符后是否能得到短单词,若可以则输出所有满足条件的1-based位置;若不行输出0。你的程序采用正向+反向扫描的逻辑,能通过给定样例,但提交后评测不通过。
你的代码核心错误分析
我看完你的代码后,发现了3个致命/严重的逻辑漏洞:
1. 未处理输入顺序反转的情况
你的代码直接假设第一个输入是长单词、第二个是短单词,但题目只说“其中一个比另一个长1”,完全可能出现输入顺序反过来的情况(比如输入a aa)。这会导致你访问数组越界(比如短单词长度比长单词大时,循环会访问long_word的超出长度的位置),触发未定义行为,直接导致评测失败。
2. 连续重复字符的边界情况处理错误
比如测试用例:
- 长单词:
aabaaa(长度6) - 短单词:
abaaa(长度5)
正确输出应该是2,然后1 2(删除第1或第2个a都能得到短单词)。但你的代码会进入forward_misspelled && !reverse_misspelled分支,直接输出1和1,漏了第二个有效位置。
3. 反向扫描的位置判断逻辑不严谨
你的反向扫描在找到第一个不匹配的位置后直接break,没有考虑到当反向全匹配时的情况(比如长单词是aaaaa,短单词是aaaa),虽然这部分你处理了,但结合正向扫描的逻辑时,对有效位置的收集范围判断错误。
修正后的代码及逻辑说明
我基于你的核心思路(正向+反向扫描),修正了所有漏洞,代码如下:
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #define MAX_LETTERS 1000000 int main(void) { char word1[MAX_LETTERS + 1]; char word2[MAX_LETTERS + 1]; scanf("%s%s", word1, word2); int len1 = strlen(word1); int len2 = strlen(word2); char *long_word, *short_word; int n; // 短单词长度 // 第一步:确保long_word是长单词,short_word是短单词,长度差必须为1 if (len1 == len2 + 1) { long_word = word1; short_word = word2; n = len2; } else if (len2 == len1 + 1) { long_word = word2; short_word = word1; n = len1; } else { // 长度差不是1,直接输出0 printf("0\n"); return 0; } // 第二步:正向扫描找第一个不匹配的位置l int l = 0; while (l < n && long_word[l] == short_word[l]) { l++; } // 第三步:反向扫描找最后一个不匹配的位置r(r是最大的索引,使得long_word[r+1] != short_word[r]) int r = n - 1; while (r >= 0 && long_word[r + 1] == short_word[r]) { r--; } // 第四步:判断有效删除位置的范围 if (l > r) { // 所有从r+1到l的0-based位置都有效,对应1-based是(r+1)+1到l+1 int count = l - r; printf("%d\n", count); for (int i = r + 1; i <= l; i++) { printf("%d ", i + 1); } printf("\n"); } else { // 检查是否存在唯一有效位置:删除l后,long_word的剩余部分等于short_word bool valid = true; // 检查long_word[l+1 ... n] 是否等于 short_word[l ... n-1] for (int i = l; i < n; i++) { if (long_word[i + 1] != short_word[i]) { valid = false; break; } } if (valid) { printf("1\n%d\n", l + 1); } else { printf("0\n"); } } return 0; }
修正后的逻辑解释
- 输入处理:先判断两个单词的长度,确保长单词和短单词的正确性,长度差不为1直接输出0。
- 正向扫描:找到第一个不匹配的位置
l,如果所有前n个字符都匹配,l会等于n。 - 反向扫描:找到最后一个不匹配的位置
r,如果所有反向对应位置都匹配,r会等于-1。 - 有效位置判断:
- 当
l > r:说明从r+1到l的所有位置,删除后都能让长单词变成短单词,输出这些位置。 - 当
l <= r:只有删除l位置(1-basedl+1)才可能有效,检查删除后的剩余部分是否完全匹配短单词,匹配则输出,否则输出0。
- 当
测试边界case
- case1:长单词
aaaaa,短单词aaaa→ 输出5,然后1 2 3 4 5(正确)。 - case2:长单词
aabaaa,短单词abaaa→ 输出2,然后1 2(正确)。 - case3:输入顺序反转
a aa→ 自动交换为长单词aa,短单词a→ 输出2,然后1 2(正确)。 - case4:长单词
abcdxxxef,短单词abcdxxef→ 输出3,然后5 6 7(符合你的样例输出)。
内容来源于stack exchange




