1. 程式人生 > >CF700E Cool Slogans SAM、線段樹合並、樹形DP

CF700E Cool Slogans SAM、線段樹合並、樹形DP

while type 通過 test stream inline 得到 一個 vector

傳送門


在最優的情況下,序列\(s_1,s_2,...,s_k\)中,\(s_i (i \in [2 , k])\)一定會是\(s_{i-1}\)的一個\(border\),即\(s_i\)同時是\(s_{i-1}\)的前綴和後綴,否則一定可以通過減去\(s_{i-1}\)的一個前綴和後綴使得滿足條件。

對原串建立\(SAM\),因為有互為後綴的條件,所以\(s_1,s_2,...,s_k\)會對應\(parent\)樹一條鏈上的若幹狀態。

發現可以在\(parent\)樹上DP。設\(f_i\)表示到達\(i\)狀態時序列的最長長度,轉移看它祖先中\(f\)最大且長度最短的串是否在當前串中出現了至少\(2\)

次。

判斷\(A\)是否在\(B\)中出現了至少兩次也不是很麻煩。處理出\(A,B\)狀態的任意一個\(endpos\),記做\(pos_A,pos_B\),然後用線段樹合並得到\(A,B\)狀態的\(endpos\)集合,那麽\(A\)\(B\)中出現了至少兩次意味著\(A\)\(endpos\)集合與\([pos_B - len_B + len_A , pos_B]\)的交集的大小\(\geq 2\)。註意到我們需要求的\(AB\)滿足\(A\)\(B\)的一個後綴,所以只需要判斷\(A\)\(endpos\)集合與\([pos_B - len_B + len_A , pos_B)\)

是否有交就可以了。

註意:\(SAM\)的一個狀態中可能有多個串,但是題目中,因為某個串出現,同一狀態的其他串也一定會在同一位置出現,所以這些串是等價的,直接取每個狀態的最長串即可。因此,可能會存在選出的\(s_i\)不是\(s_{i-1}\)\(border\),但並不會影響答案的大小。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
//This code is written by Itst
using namespace std;

const int MAXN = 4e5 + 7;

namespace segtree{
    struct node{
        int l , r , sz;
    }Tree[MAXN << 5];
    int rt[MAXN] , cnt;

#define lch Tree[x].l
#define rch Tree[x].r
#define mid ((l + r) >> 1)
    int insert(int t , int l , int r , int tar){
        int x = ++cnt;
        Tree[x] = Tree[t];
        ++Tree[x].sz;
        if(l == r) return x;
        if(mid >= tar) lch = insert(lch , l , mid , tar);
        else rch = insert(rch , mid + 1 , r , tar);
        return x;
    }

    int merge(int p , int q){
        if(!p || !q) return p + q;
        int x = ++cnt;
        Tree[x].sz = Tree[q].sz + Tree[p].sz;
        lch = merge(Tree[p].l , Tree[q].l);
        rch = merge(Tree[p].r , Tree[q].r);
        return x;
    }

    bool query(int x , int l , int r , int L , int R){
        if(!Tree[x].sz) return 0;
        if(l >= L && r <= R) return 1;
        if(mid >= L && query(lch , l , mid , L , R)) return 1;
        return mid < R && query(rch , mid + 1 , r , L , R);
    }
}

using segtree::rt; using segtree::merge; using segtree::query;

namespace SAM{
    int Lst[MAXN] , Sst[MAXN] , fa[MAXN] , trans[MAXN][26] , endpos[MAXN];
    int cnt = 1 , lst = 1 , L;
    char s[MAXN];

    void insert(int len , int x){
        int t = ++cnt , p = lst;
        endpos[t] = Lst[lst = t] = len;
        while(p && !trans[p][x]){
            trans[p][x] = t;
            p = fa[p];
        }
        if(!p){Sst[t] = fa[t] = 1; return;}
        int q = trans[p][x];
        Sst[t] = Lst[p] + 2;
        if(Lst[q] == Lst[p] + 1){fa[t] = q; return;}
        int k = ++cnt;
        memcpy(trans[k] , trans[q] , sizeof(trans[k]));
        Lst[k] = Lst[p] + 1; Sst[k] = Sst[q];
        Sst[q] = Lst[p] + 2;
        fa[k] = fa[q]; fa[q] = fa[t] = k;
        while(trans[p][x] == q){
            trans[p][x] = k;
            p = fa[p];
        }
    }

    void init(){
        scanf("%d %s" , &L , s + 1);
        for(int i = 1 ; i <= L ; ++i)
            insert(i , s[i] - 'a');
    }

    vector < int > ch[MAXN];
    int ans = 0 , top[MAXN] , len[MAXN];
    
    void dfs(int x){
        if(endpos[x]) rt[x] = segtree::insert(rt[x] , 1 , L , endpos[x]);
        for(auto t : ch[x]){
            dfs(t);
            if(!endpos[x]) endpos[x] = endpos[t];
            rt[x] = merge(rt[x] , rt[t]);
        }
    }

    void dp(int x){
        if(fa[x])
            if(fa[x] == 1 || query(rt[top[fa[x]]] , 1 , L , endpos[x] - Lst[x] + Lst[top[fa[x]]] , endpos[x] - 1)){
                len[x] = len[fa[x]] + 1;
                top[x] = x;
                ans = max(ans , len[x]);
            }
            else{
                len[x] = len[fa[x]];
                top[x] = top[fa[x]];
            }
        for(auto t : ch[x]) dp(t);
    }
    
    void work(){
        for(int i = 2 ; i <= cnt ; ++i)
            ch[fa[i]].push_back(i);
        dfs(1);
        dp(1);
        cout << ans;
    }
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("in" , "r" , stdin);
    //freopen("out" , "w" , stdout);
    #endif
    SAM::init(); SAM::work();
    return 0;
}

CF700E Cool Slogans SAM、線段樹合並、樹形DP