BZOJ2882 工艺【SAM】 最小循环串

BZOJ2882 工艺

给出一个串,要求其循环同构串中字典序最小的那个
串翻倍建\(SAM\)然后从起点开始贪心的跑\(n\)次即可

当然也能用最小表示法来做

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 6e5+7;
int n,A[MAXN];
struct SAM{
    int len[MAXN<<1],link[MAXN<<1],tot,last;
    map<int,int> ch[MAXN<<1];
    SAM(){ link[len[0] = tot = last = 0] = -1; }
    void extend(int x){
        int np = ++tot, p = last; len[np] = len[last] + 1;
        while(p!=-1 and !ch[p].count(x)){
            ch[p].insert(make_pair(x,np));
            p = link[p];
        }
        if(p==-1) link[np] = 0;
        else{
            int q = ch[p].at(x);
            if(len[p]+1==len[q]) link[np] = q;
            else{
                int clone = ++tot;
                link[clone] = link[q];
                ch[clone] = ch[q];
                len[clone] = len[p] + 1;
                while(p!=-1 and ch[p].count(x) and ch[p].at(x)==q){
                    ch[p].at(x) = clone;
                    p = link[p];
                }
                link[q] = link[np] = clone;
            }
        }
        last = np;
    }
    void minimal_circle(){
        int u = 0;
        for(int i = 1; i <= n; i++){
            printf("%d ",ch[u].begin()->first);
            u = ch[u].begin()->second;
        }
    }
}sam;
int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) scanf("%d",&A[i]);
    for(int i = 1; i <= n; i++) sam.extend(A[i]);
    for(int i = 1; i <= n; i++) sam.extend(A[i]);
    sam.minimal_circle();
    return 0;
}
上一篇:CF1037H Security(SAM+线段树合并)


下一篇:[HAOI2016]找相同字符(广义SAM)