GDCPC廣東省賽-C-Conspicuousness題解


本蒟蒻屑的第一篇題解博客Qwq

原題鏈接

題目翻譯:給定一個由1-9組成的長度范圍為(3e5)的字符串,並給出q(q <= 3e5)次詢問,每次給出個長度k, 求出該字符串中長度至少為k的子串的最長長度。

省賽的C題,比賽時讀題發現應該是一個后綴自動機但由於本屑非oier,對於xx自動機也只是聽過沒有了解,當場就放棄了,近幾天學了SAM的黑盒使用后,終於A了此題。

思路:眾所周知,對一個字符串建起來SAM后,SAM上每個結點都有個l數組,l數組代表的是該節點所包含的字符串集合中,最長字符串的長度,而每個節點有size數組,表示該字符串集合出現的次數。這時候就有個較為朴素的思路:對於每次詢問,枚舉所有tot的節點i,當l[i] >= k時,則ans = max(l[i], ans); 但這樣明顯時間復雜度會超,因此,我們可以預處理出每個點的答案,最后一並詢問。

我們開兩個數組,dp數組和ans數組,其中ans數組為我們要求的答案,而dp數組定義如下:

dp[i] 表示 ans[1] ~ ans[i] 至少應該為幾,

在枚舉每個節點的時候,該節點代表的字符串集合中,最長的長度為l[i],則ans[1] ~ ans[l[i]] 至少為size[i];

則有如下代碼:

for (int i = 1; i <= tot; i++) {
    dp[l[i]] = max(dp[l[i]], (ll)size[i]);
}

之后,我們一步一步求ans數組

顯然有,dp[i]為長度為i的子串最大出現次數

則有從后往前推的遞推式,ans[i] = max(dp[i], ans[i + 1]);

則有如下代碼

for (int i = n - 1; i >= 1; i--) {
    ans[i] = max(dp[i], ans[i + 1]);
}

完整代碼如下

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5 + 10;
char s[N];
int n, K, q;
ll dp[N];
ll ans[N];
struct SuffixAutoMaton {
    int ch[N << 1][10], fa[N << 1],l[N << 1],size[N << 1],k[N << 1],c[N << 1];
    int last, tot;
    void init() { last = tot = 1;memset(ch[1], 0, sizeof ch[1]); }
    void ins(int c,int pos) {
        int p = last,np = ++tot;last = np;l[np] = l[p] + 1;
        memset(ch[tot],0,sizeof ch[tot]);
        for(;p && !ch[p][c]; p = fa[p])ch[p][c] = np;
        if(!p)fa[np] = 1;
        else {
            int q = ch[p][c];
            if(l[p] + 1 == l[q]) fa[np] = q;
            else {
                int nq = ++tot; l[nq] = l[p] + 1;
                memcpy(ch[nq], ch[q], sizeof(ch[q]));
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
            }
        }
        size[np] = 1;
    }
    void build() {
        init();
        int n = strlen(s + 1);
        for(int i = 1;i <= n; i++) ins(s[i]-'0',i);
        for(int i = 1;i <= tot; i++) c[l[i]]++;
        for(int i = 1;i <= tot; i++) c[i] += c[i-1];
        for(int i = 1;i <= tot; i++) k[c[l[i]]--] = i;
        for(int i = tot; i >= 1; i--) {
            int id = k[i];
            size[fa[id]] += size[id];
        }
        for (int i = 1; i <= tot; i++) {
            dp[l[i]] = max(dp[l[i]], (ll)size[i]);  // 求出dp數組。
        }
    }
}sam;
int main() {
    cin >> n >> K;
    scanf("%s", s + 1);
    sam.build();
    ans[n] = dp[n];
    for (int i = n - 1; i >= 1; i--) {
        ans[i] = max(dp[i], ans[i + 1]); //求出ans數組。
    }
    for (int i = 1; i <= K; i++) {
        scanf("%lld", &q);
        printf("%lld\n", ans[q]);
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM