Not Found


CST PA3

3-2 Not Found

算法构思

注意到 $A$ 的输入长度范围为 $[1, 2^{24}]$, 而可由 $A$ 产生的所有长度为 $n$ 的子串数为 $\vert A\vert - n + 1$. 一个长度为 $n$ 的字符串共有 $2^n$ 种可能, 当 $n = 24$ 时, 由 $\vert A\vert - 24 + 1 < 2^{24}$, $A$ 中已经无法包含所有长度为 $24$ 的子串, 因此第一个未在 $A$ 中出现的子串必是长度不超过 $24$ 的子串.

考虑枚举所有长度为 $k$ 的子串, 其中 $k \le \min(\vert A\vert, 24)$. 对于 $A$ 中长度为 $k$ 的所有子串, 我们依据其二进制值进行标记. 由于数据范围过大, 且实际上需储存的只是串的存在性, 因此考虑使用 Bitmap 对子串情况进行存储.

class Bitmap

// Line 6
// 实现一个 Bitmap 类存储 01 字符串.
class Bitmap {
public:
    // 所存放的空间 M[], 容量为 N * sizeof(char) * 8 bit.
    unsigned char* M;
    int N;  
    // 初始化 Bitmap, 置 0.
    void init(int n) {
        N = (n + 7) / 8;
        M = new unsigned char[N];
        memset(M, 0, N);
    }
    // 构造函数.
    Bitmap(int n = 8) { init(n); }
    // 设置 Bitmap 第 k 位为 1.
    void set(int k) { M[k >> 3] |= (0x80 >> (k & 0x07)); }
    // 清除 Bitmap 第 k 位.
    void clear(int k) { M[k >> 3] &= ~ (0x80 >> (k & 0x07)); }
    // 检测 Bitmap 第 k 位是否为 1.
    bool test(int k) { return M[k >> 3] & (0x80 >> (k & 0x07)); }
    // 清零 Bitmap. 
    void clean() { memset(M, 0, N); }
};

void read_input()

在读入初始字符串时, 设置 Bitmap* str, 若初始字符串第 $k$ 位为 $1$, 则使用 str->set[k] 修改 Bitmap 的内容. 将输入字符串从左到右读入 Bitmap* str, 此时 Bitmap* str 存放了一个 $01$ 字符串.

// Line 32
void read_input(Bitmap* str, char& input, int& len) {
    while(input != '\n'){
        input = getchar();
        if(input == '1') 
            str->set(len);
        len++;
    }
}

int read()

// Line 42
int read(Bitmap* bitmap, int pos, int read_len) {
    int tmp = 0;
    for (int i = 0; i <= read_len - 1; i++)
        tmp = bitmap->test(pos + i) ? (tmp << 1) + 1 : tmp << 1;
    return tmp;
}

read()Bitmap* strpos 位开始读取为长 read_len 的二进制串, 并将其转换为十进制数. 为了节省遍历子串的时间, 首先考虑 $k = \min(\vert A\vert, 24)$ 的情形.

...
// Line 113
int k = search_len;
Bitmap* a = new Bitmap(1 << search_len);
Bitmap* b = new Bitmap(1 << (search_len - 1));
...

Bitmap* a 用于存储所有长 $k$ 位的 $01$ 串在原字符串中的出现情况, 若 a->test(i) == true, 则输入串中存在十进制值为 $i$ 的 $k$ 位 $01$ 二进制串. Bitmap* b 用于存储所有长 $k - 1$ 位的 $01$ 串在原字符串中的出现情况, 若 b->test(j) == true, 则输入串中存在十进制值为 $j$ 的 $k - 1$ 位 $01$ 二进制串.

Bitmap* str 的末尾开始向头部遍历, 对于当前处理的二进制串的十进制值 $s = \overline{a_{m + 1}a_{m + 2}\cdots a_{m + k}}_2$, 下一个处理串的十进制值 $s’ = \overline{a_ma_{m + 1}\cdots a_{m + k - 1}}_2 = a\gg 1 + a_m\ll (k - 1)$.

从后向前遍历, 节省了将二进制串转换为十进制串的时间.

...
// Line 118
int pos = len - k;
int get = read(str, pos, k);
a->set(get);
b->set(get >>= 1);
while (--pos >= 0) {
    if (str->test(pos))
        get += 1 << (k - 1);
    a->set(get);
    b->set(get >>= 1);
}
set_end(str, b, len - k, k);
...

为此, 首先读入 str 末端的 $k$ 长子串 get, 调用 a->set(get) 将其存储在 Bitmap* a 中, 并使用一个 while 循环遍历接下来所有 $k$ 长子串.

void set_end()

// Line 68
void set_end (Bitmap* str, Bitmap* a, int pos, int k) {
    a->set(read(str, pos + 1, k - 1));
}

调用 set_end()str 末尾的 $k - 1$ 位 $01$ 子串加入 Bitmap* b.

考虑到长度为 $k - 1$ 的子串. 对于所有 $k$ 长子串的十进制值 $S_1, S_2, \cdots, S_{\vert A\vert - k + 1}$, 长度为 $k - 1$ 的子串恰为所有 $k$ 长子串删去最后一位, 再补上 str 中末端的 $k - 1$ 长子串, 其十进制值 $R_1 = S_1\gg 1, R_2 = S_2\gg 1, \cdots, R_{\vert A\vert - k + 1} = S_{\vert A\vert - k + 1}\gg 1, R_{\vert A\vert - k + 2}$, 其中 $R_{\vert A\vert - k + 2}$ 调用 set_end(str, b, len - k, k) 来设置.

接下来调用 check_full(b, k) 检查 Bitmap* b 中是否已经存储了所有 $k - 1$ 长子串.

void get_ans()

// Line 82
void get_ans (Bitmap* a, int k, int& output, int& precision) {
    for (int i = 0; i < (1 << k); i++)
        if (!a->test(i)) {
            output = i;
            precision = k;
            break;
        }
}

若所有 $k - 1$ 长子串都已经存储在了 Bitmap* b 中, 那么说明 str 中首个未出现的字符串长度为 $k$, 只需调用 get_ans(a, k, output, precision)Bitmap* a 中遍历寻找首个未出现的 $k$ 长子串, 并调用 show_ans(output, precision) 输出在原串中未出现的最短且字典序最小的 $k$ 长子串即可.

void trans()

// Line 54
void trans (Bitmap* a, Bitmap* b, int k) {
    for (int i = 0; i < (1 << k); i++)
        if (a->test(i))
            b->set(i >> 1);
}

否则, 若存在 $k - 1$ 长子串未存储在 Bitmap* b 中, 那么调用 copy(a, b)Bitmap* b 的值赋给 Bitmap* a, k 自减, 并调用 b->clean()Bitmap* b 置零. 此时 Bitmap* a 存储了所有 $k$ 长子串, 并调用 trans(a, b, k) 利用其中值更新 Bitmap* b 中的 $k - 1$ 长子串, 仍然需要使用 set_end(str, b, len - k, k)str 末端的子串加入 b.

当遍历到长度为 $k - 1$ 的串全部存在, 而 $k$ 长子串存在缺失时, 将 $k$ 长子串所缺少十进制值对应的首个 $01$ 子串输出.

复杂度估计

时间复杂度

第一次读入串 str 的时间复杂度为 $O(\vert A\vert)$;

对于子串的处理考虑极端情况, 对 $k$ 长子串存储、遍历枚举、更新所需要时间为 $O(2^k)$, 若从长 $24$ 子串遍历至长 $1$ 子串, 消耗时间为 $O(2^{25})$.

综上, 算法最坏时间复杂度为 $O(\vert A\vert + 2^{25})$.

空间复杂度

空间复杂度主要来自于使用了 Bitmap 存储目的串的过程.

最坏情况下开辟了两个可存储 $2^{24}$ bit, 一个可存储 $2^{23}$ bitBitmap, 最坏情况空间复杂度为: $(2\times 2^{24} + 2^{23}) bit = 5\times 2^{20} B = 5MB$, 在题目要求范围内.


文章作者: Chengsx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chengsx !
  目录