計蒜客 - 匹配格式

計蒜客 - 匹配格式

有一字符串 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 )、微信公衆號( 凝神長老和他的朋友們 )
凝神長老的二維碼們

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