记录一个 Stack Overflow 实例

简单记录一个因为我的写法问题很容易想不到的 RE 错误。

具体错误是这样的,我写了一个代码:

错误的 Code
  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
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// author : black_trees

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

#define endl '\n'

using namespace std;
using i64 = long long;

const int si = 2e5 + 10;

class StringWithDoubleHash {
private: 
    string s; i64 len;
    i64 h[2][si], power[2][si];
    const i64 base = 131;
    const i64 m[2] = {998244853ll, 1000000009ll};
public:
    int Size() { return len; }
    i64 Index(char ch) { return (i64)(ch - 'a'); }
    void Init(string t) {
        s = ' ' + t, len = (int)t.size();
        power[0][0] = power[1][0] = 1ll;
        for(int _ = 0; _ <= 1; ++_) {
            for(int i = 1; i <= len; ++i) {
                power[_][i] = power[_][i - 1] * base % m[_];
            }
        }
        h[0][0] = h[1][0] = 0ll;
        for(int _ = 0; _ <= 1; ++_) {
            for(int i = 1; i <= len; ++i) {
                h[_][i] = (h[_][i - 1] * base) % m[_] + Index(s[i]) % m[_];
            }
        }
    }
    i64 Query(int _, int l, int r) {
        return (h[_][r] - (h[_][l - 1] * power[_][r - l + 1] % m[_]) + m[_]) % m[_];
    }
    bool Equal(int l1, int r1, int l2, int r2) {
        bool f = true;
        for(int _ = 0; _ <= 1; ++_) {
            f &= (Query(_, l1, r1) == Query(_, l2, r2));
        }
        return f;
    }
};

using str = StringWithDoubleHash;
bool Equal(str a, str b, int l, int r) {
    if(a.Size() != b.Size()) return false;
    bool f = true;
    for(int i = 0; i <= 1; ++i) 
        f &= (a.Query(i, l, r) == b.Query(i, l, r));
    return f;
}

int Radius(str s, int l, int r, int c) {
    while(l < r) {
        int mid = (l + r + 1) >> 1;
        if(c - mid + 1 >= 1 && c + mid - 1 <= s.Size() 
        && s.Equal(c - mid + 1, c, c, c + mid - 1))
            l = mid;
        else r = mid - 1;
    }
    return l;
}
int Radius_ignore(str s, int l, int r, int c, int Ra) {
    while(l < r) {
        int mid = (l + r + 1) >> 1;
        if(c - mid + 1 >= 1 && c + mid - 1 <= s.Size()
        && s.Equal(c - mid + 1, c - Ra - 1, c + Ra + 1, c + mid - 1))
            l = mid;
        else r = mid - 1;
    }
    return l;
} 
str s;

int Rad[si];
int delta[si][27];
int pre[si], suf[si], cnt[si];
void fix(int a[], int l, int r, int v) {
    a[r] += v, a[l - 1] -= v;
}
void redo(int a[], int n) { 
    for(int i = 1; i <= n; ++i)
        a[i] = a[i] + a[i + 1];
} 

int main() {

    cin.tie(0) -> sync_with_stdio(false);
    cin.exceptions(cin.failbit | cin.badbit);

    string tmp; cin >> tmp;
    string t = "#";
    for(int i = 0; i < (int)tmp.size(); ++i)
        t += tmp[i], t += '#';
    s.Init(t), t = ' ' + t;

    // cout << t << endl;

    int n = s.Size(), sum = 0;
    for(int i = 1; i <= n; ++i) {
        Rad[i] = Radius(s, 1, n, i);
        sum = sum + (Rad[i] / 2);
        int L = i - Rad[i] + 1, R = i + Rad[i] - 1;

        // loosen
        if(L - 1 >= 1 && R + 1 <= n) {
            char ch1 = t[L - 1], ch2 = t[R + 1];
            int Rr = Radius_ignore(s, Rad[i], n, i, Rad[i]);
            delta[L - 1][(int)(ch2 - 'a' + 1)] += Rr - Rad[i];
            delta[R + 1][(int)(ch1 - 'a' + 1)] += Rr - Rad[i];
        }

        // lessen
        fix(pre, L, i, L), fix(suf, i, R, R), fix(cnt, L, R, 1);
    }
    redo(pre, n), redo(suf, n), redo(cnt, n);

// for(int i = 1; i <= n; ++i) cout << Rad[i] << endl;

    int ans = -1;
    for(int i = 1; i <= n; ++i) {
        if(t[i] == '#') continue;
        for(char ch = 'a'; ch <= 'z'; ++ch) {
            int add = delta[i][(int)(ch - 'a' + 1)];
            int sub = pre[i] + suf[i] - (i * cnt[i]);
            ans = max(ans, sum + (add - sub) / 2);
        }
    }

    cout << ans << endl;

    return 0;
}

// ()()()(?

但是编译后运行会直接返回 SIGSEGV,使用 gdb 调试后出来了这样的信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Reading symbols from T4...
(gdb) r
Starting program: C:\Users\Administrator\OneDrive\Workspace\Code\Mockcontests\20230201C\T4.exe
[New Thread 8836.0x23e0]
[New Thread 8836.0x2cd4]
[New Thread 8836.0x2a90]

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ff7b6ec29c6 in ___chkstk_ms ()
(gdb) q

我经过查找发现了这个函数的源代码:

1
/* ___chkstk_ms is a *special* function call, which uses %rax as the argument.We avoid clobbering any registers.  Unlike ___chkstk, it just probes the stack and does no stack allocation.  */

功能大致是在分配一个新的栈之后进行一些检查

猜测是申请栈空间的时候触发了什么错误,但是具体并不清楚,网上也没有类似的原因。

而且因为是运行就 RE 了,我猜测大概率问题出在 class StringWithDoubleHash 当中,因为我之前封装的时候就出过类似的运行就挂的问题,但是并没有看出来,因为测了一下好像空间也没有爆炸之类的。

然后我发了一个帖子询问,还在 uoj 群问了一下。

很感谢 @Zyingyzzz 和 @阮行止 神仙发现了问题。

问题出在我的 Radius 函数,我传入了一个 StringWithDoubleHash 类型的成员,大小是 1e5 级别,然后这里直接复制,所以就 stack overflow 了。

解决方法大概是不然就不传成员,不然就传引用。

这个对我来说很容易写错,所以记录一下:

正确的 Code
  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
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// author : black_trees

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>

#define endl '\n'

using namespace std;
using i64 = long long;

const int si = 2e5 + 10;

class StringWithDoubleHash {
private: 
    string s; i64 len;
    i64 h[2][si], power[2][si];
    const i64 base = 131;
    const i64 m[2] = {998244853ll, 1000000009ll};
public:
    int Size() { return len; }
    i64 Index(char ch) { return (i64)(ch - 'a'); }
    void Init(string t) {
        s = ' ' + t, len = (int)t.size();
        power[0][0] = power[1][0] = 1ll;
        for(int _ = 0; _ <= 1; ++_) {
            for(int i = 1; i <= len; ++i) {
                power[_][i] = power[_][i - 1] * base % m[_];
            }
        }
        h[0][0] = h[1][0] = 0ll;
        for(int _ = 0; _ <= 1; ++_) {
            for(int i = 1; i <= len; ++i) {
                h[_][i] = (h[_][i - 1] * base) % m[_] + Index(s[i]) % m[_];
            }
        }
    }
    i64 Query(int _, int l, int r) {
        return (h[_][r] - (h[_][l - 1] * power[_][r - l + 1] % m[_]) + m[_]) % m[_];
    }
    bool Equal(int l1, int r1, int l2, int r2) {
        bool f = true;
        for(int _ = 0; _ <= 1; ++_) {
            f &= (Query(_, l1, r1) == Query(_, l2, r2));
        }
        return f;
    }
};

using str = StringWithDoubleHash;
bool Equal(str &a, str &b, int l, int r) {
    if(a.Size() != b.Size()) return false;
    bool f = true;
    for(int i = 0; i <= 1; ++i) 
        f &= (a.Query(i, l, r) == b.Query(i, l, r));
    return f;
}

int Radius(str &s, int l, int r, int c) {
    while(l < r) {
        int mid = (l + r + 1) >> 1;
        if(c - mid + 1 >= 1 && c + mid - 1 <= s.Size() 
        && s.Equal(c - mid + 1, c, c, c + mid - 1))
            l = mid;
        else r = mid - 1;
    }
    return l;
}
int Radius_ignore(str &s, int l, int r, int c, int Ra) {
    while(l < r) {
        int mid = (l + r + 1) >> 1;
        if(c - mid + 1 >= 1 && c + mid - 1 <= s.Size()
        && s.Equal(c - mid + 1, c - Ra - 1, c + Ra + 1, c + mid - 1))
            l = mid;
        else r = mid - 1;
    }
    return l;
} 
str s;

int Rad[si];
int delta[si][27];
int pre[si], suf[si], cnt[si];
void fix(int a[], int l, int r, int v) {
    a[r] += v, a[l - 1] -= v;
}
void redo(int a[], int n) { 
    for(int i = 1; i <= n; ++i)
        a[i] = a[i] + a[i + 1];
} 

int main() {

    cin.tie(0) -> sync_with_stdio(false);
    cin.exceptions(cin.failbit | cin.badbit);

    string tmp; cin >> tmp;
    string t = "#";
    for(int i = 0; i < (int)tmp.size(); ++i)
        t += tmp[i], t += '#';
    s.Init(t), t = ' ' + t;

    // cout << t << endl;

    int n = s.Size(), sum = 0;
    for(int i = 1; i <= n; ++i) {
        Rad[i] = Radius(s, 1, n, i);
        sum = sum + (Rad[i] / 2);
        int L = i - Rad[i] + 1, R = i + Rad[i] - 1;

        // loosen
        if(L - 1 >= 1 && R + 1 <= n) {
            char ch1 = t[L - 1], ch2 = t[R + 1];
            int Rr = Radius_ignore(s, Rad[i], n, i, Rad[i]);
            delta[L - 1][(int)(ch2 - 'a' + 1)] += Rr - Rad[i];
            delta[R + 1][(int)(ch1 - 'a' + 1)] += Rr - Rad[i];
        }

        // lessen
        fix(pre, L, i, L), fix(suf, i, R, R), fix(cnt, L, R, 1);
    }
    redo(pre, n), redo(suf, n), redo(cnt, n);

// for(int i = 1; i <= n; ++i) cout << Rad[i] << endl;

    int ans = -1;
    for(int i = 1; i <= n; ++i) {
        if(t[i] == '#') continue;
        for(char ch = 'a'; ch <= 'z'; ++ch) {
            int add = delta[i][(int)(ch - 'a' + 1)];
            int sub = pre[i] + suf[i] - (i * cnt[i]);
            ans = max(ans, sum + (add - sub) / 2);
        }
    }

    cout << ans << endl;

    return 0;
}

// ()()()(?

虽然这份还是没过原题就是了……哭。


最后更新: February 2, 2023