计蒜客 - 匹配格式

计蒜客 - 匹配格式

有一字符串 S,蒜头君想在 S 中找到最长的子串 E,使得 S 满足格式 “EAEBE”,其中 A, B 可以为任意的 S 子串。也就是说子串 E 既是 S 的前缀也是 S 的后缀,同时还在 S 中间出现,但不与前缀 E 与后缀 E 重叠。

输入格式

输入一个字符串 S,由小写字母构成,长度不超过 10610^6

aaxoaaaaa

输出格式

答案输出占一行,输出一个整数,表示子串 E 的长度。

2

因为求的是最长的 E,而按照题意,E 不能重叠,所以 E 最长就是 length / 3(此时 AB 都是空串),因此从大到小枚举所有可能的长度 len 就可以了。

for (int len = length / 3; len >= 1; len--) {

tlen + 1 开始枚举,最多到 length - 2 * len,因为后面的 EBE 最少需要占用 len + len 个长度,此时 B 是空串。

for (int t = len + 1; t <= length - 2 * len; t++) {

对于每一个枚举的 tnext[t] 表示从该字符 s[t] 开始,有长度为多少的字符串,是与整个字符串开头的那么多个字符串相等的,显然,为了满足题目要求,next[t] 必须大于 len

if (next[t] < len) {
    continue; // 长度不满足要求,继续
}

现在,我们枚举了开头长度为 lenE,并且在中间那段找到了一个 t 开始的位置,可以保证有大于或者等于 len 个字符与字符串开头是匹配的,所以,我们只需要需要找到最后一个 E

由于题目要求最后一个 E 必须是字符号结尾,所以直接判断 next[length - len + 1] 是不是大于等于 len 就可以了。

if (next[length - len + 1] < len) {
    continue;
}

如果满足,那么说明当前的 len 就是答案。在整个循环中保留最大的 len 即可。

ans = max(ans, len);

事实上,得到了一个答案,直接输出就可以停止了,不需要继续枚举更短的 len

另外注意到 if (next[length - len + 1] < len) 判断与 t 无关,所以提到循环外面,否则可能会超时。

#include <bits/stdc++.h>

#define MAX_LEN 1000007

using namespace std;

void print(const char *t, const int *next, int n) {
    for (int i = 1; i <= n; i++) {
        printf("%c%c", t[i], i == n ? '\n' : '\t');
    }
    for (int i = 1; i <= n; i++) {
        printf("%d%c", next[i], i == n ? '\n' : '\t');
    }
}

int *getNext(const char *t, int n) {
    int *next = (int *) malloc(sizeof(int) * (n + 1));
    next[1] = n;
    int p = 1;
    while (p < n && t[p] == t[p + 1]) p++;
    next[2] = p - 1;
    int k = 2, l;
    for (int i = 3; i <= n; i++) {
        p = k + next[k] - 1;
        l = next[i - k + 1];
        if (i + l <= p) next[i] = l;
        else {
            int j = p - i + 1;
            if (j < 0) j = 0;
            while (i + j <= n && t[i + j] == t[j + 1]) j++;
            next[i] = j;
            k = i;
        }
    }
    return next;
}

int main() {
    char s[MAX_LEN] = {0};
    scanf("%s", s + 1);
    int length = strlen(s + 1);
    int *next = getNext(s, length);
    for (int len = length / 3; len >= 1; len--) {
        if (next[length - len + 1] < len) {
            continue;
        }
        for (int t = len + 1; t <= length - 2 * len; t++) {
            if (next[t] < len) {
                continue; // 长度不满足要求,继续
            }
            printf("%d\n", len);
            return 0;
        }
    }
    return -1;
}

欢迎关注我的个人博客以阅读更多优秀文章:凝神长老和他的朋友们(https://www.jxtxzzw.com)

也欢迎关注我的其他平台:知乎( https://s.zzw.ink/zhihu )、知乎专栏( https://s.zzw.ink/zhuanlan )、哔哩哔哩( https://s.zzw.ink/blbl )、微信公众号( 凝神长老和他的朋友们 )
凝神长老的二维码们

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章