关于LeetCode评测的机制
2022年9月2日,天气忘记怎么样了,反正我在图书馆摸鱼的时候一时兴起想到了LeetCode第三题”无重复字符的最长子串”的算法,就随手写了。
不得不说很多时候还是要面向对象比较好写,所以定义了一个CharSet类,顺便就创建了一个charset对象。
蜜汁失败
我觉得我算法没问题了,所以拿默认测例跑了一下,顺便处理好边界情况。看着这边测试也没问题,我就点了提交。
结果在”bbbbb”这个测例莫名其妙给我报错,预计结果为1,但我的代码跑出来是0。因为对自定义测例的掌握还不太熟练,我脑子里跑了一下,觉得没问题。
为了排除评测机抽风的极小概率事件,我原样重新提交了一遍,结果还是没过。
等我折腾了几遍终于学会了手动设置测例,接着提交,发现”bbbbb”这个手动设置的测例正常输出了1的结果。我再次怀疑起是否存在在编译时宇宙粒子改变了评测机中的某几个bit的可能性,又交了一遍,结果证明:当前地球的辐射水平应该还是比较正常的。
原因分析(虚假的原因)
考虑到输出的结果0与我初始化变量的值一致,我怀疑是否是代码没有顺利进入循环。为此,我偷偷把初始化值改成了-1,
果然这个测例给我跑出了-1的结果。但我死活想不到为什么它不进循环。
解决的过程(应该是真实的原因)
在我不断检查代码的时候,我开始思考是否是因为定义对象的问题,毕竟这次是第一次在LeetCode里用面向对象写。
我意识到定义完类之后顺便创建的对象是在LeetCode的函数外面的,转而意识到这个对象是全局变量。我试着把对象的声明放进函数内部,然后就过了。
进一步分析
引出一个猜想,LeetCode的评测应当是在一次运行中多次调用其设置的函数接口来实现的。意即,两次调用间,全局变量不会被析构,这在我的算法中
就会影响最终的结果:每次计算前,我的对象是需要初始化的,这个初始化过程被我封装在构造函数中,只在对象创建时被调用。
“ddddd”可能是所有测例中的第二个,在正常运行完第一个测例后由于没有重置charset对象,影响了第二个测例的结果。
因此,在写题的时候还要考虑对象的生命周期问题,对这种情形,应当将对象的声明放在函数内部,使其在函数被调用时被构造,返回时被析构,从而执行构造函数中的初始化。
或者,其实也可以不把初始化放在构造函数中,而是另外定义一个初始化函数,在使用前调用初始化。
代码附上
- 问题代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class CharSet {
int situ[128];
public:
CharSet(){
for(int i = 0; i < 128; i++){
situ[i] = -1;
}
}
int AddInfo(char c, int si){
int index = (int)c;
int ret = situ[index];
situ[index] = si;
return ret;
}
} charset;
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int slen = s.length();
int Max_Length = -1;
int Current_Length = 0;
int le = 0, ri = 0;
int sig;
//线性读取
for(ri = 0; ri < slen; ri ++){
sig = charset.AddInfo(s[ri], ri);
if(sig >= le){
le = sig + 1;
}
Current_Length = ri - le + 1;
if(Current_Length >= Max_Length){
Max_Length = Current_Length;
}
}
return Max_Length;
}
}; - 通过代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40class CharSet {
int situ[128];
public:
CharSet(){
for(int i = 0; i < 128; i++){
situ[i] = -1;
}
}
int AddInfo(char c, int si){
int index = (int)c;
int ret = situ[index];
situ[index] = si;
return ret;
}
};
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int slen = s.length();
int Max_Length = -1;
int Current_Length = 0;
int le = 0, ri = 0;
int sig;
//线性读取
for(ri = 0; ri < slen; ri ++){
CharSet charset;
sig = charset.AddInfo(s[ri], ri);
if(sig >= le){
le = sig + 1;
}
Current_Length = ri - le + 1;
if(Current_Length >= Max_Length){
Max_Length = Current_Length;
}
}
return Max_Length;
}
};