[CF700E](Cool Slogans)

QY 大神仙:这就是SAM大水题,快切快切

然而还没怎么理解SAM的我表示对着题解一脸懵逼

设原串为s;

对于后缀自动机上的一个点i,定义dp[i]为点i表示的字符串的答案(最长嵌套次数)

显然点i应从fa[i],fa[fa[i]]等endpos包含endpos(i)的节点转移过来,为什么呢?

因为后缀自动机的性质,fa[i]所代表的字符串都是i所代表的字符串的后缀(endpos),如果答案(嵌套的字符串)不包含s[endpos(i)]的话,就不用i去更新。

举个栗子(from here)

 

这里的4号节点,也就是(len最长的字符串)aab,它的father是1,也就是意味着它的答案没法更新(dp[4]=1)

可是aab里面有两个a,理论上答案是2,但因为3号节点(也就是aa)已经能通过fa[3]=2号节点来转移达到dp[3]=2;

所以4号节点需要管的转移只有它的后缀,也就是它的fa所包含的字符,真的只是fa[i]吗?注意到其实是fa[i],fa[fa[i]],fa[fa[fa[i]]]等fa树上一路跳上去的节点

好,那么我们得出(其实都是博主在瞎扯)点i只需从fa[i],fa[fa[i]]等节点转移过来,

由于字符串i嵌套字符串fa[i]肯定比嵌套字符串fa[fa[i]]难,所以我们记top[i]为i的fa树路径上最浅的dp值等于dp[i]的位置,也就是说从top[i]到i都没有嵌套来更新dp值

那么i就可以从top[fa]转移过来而不是从fa树上一路跳上去更新

那么还剩下具体的更新部分,也就是怎么判节点top[fa[a]]代表的字符串在节点a代表的字符串中出现了两次呢?

这里我们用endpos来判断,如果top[fa[a]]的至少两个endpos在a代表的最长字符串所覆盖的区间里出现过就成立

因为top[fa[a]]是a的后缀,所以肯定已经出现了一次,所以只要在(pos[a]-len[a]+len[top[fa[a]]],pos[a]-1)中出现过一次即可

等等,这是什么式子?

这里 pos[a]为a代表的最长字符串的最后一个字符在原串s中的位置,len[a]为a代表的最长字符串的长度

那么pos[a]-len[a]+1 到pos[a]即a代表的最长字符串所覆盖的区间,当然top[fa[a]]的endpos当然是不能在区间前len[top[fa[a]]-1的点出现的,不然还是越界

又因为top[fa[a]]的endpos在pos[a]肯定出现过一次所以要把这个点舍掉,那么这就是整个式子了

至于怎么维护一个节点的endpos集合,线段树合并即可。

(扯完了,上code):

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 400004
#define mid (l+r>>1)
using namespace std;
struct seg {
    int L[N*20],R[N*20],tot;
    void init() {
        tot=0;
        }
    void insert(int &now,int l,int r,int pos) {
        if(!now)now=++tot;
        if(l==r)return;
        if(pos<=mid)insert(L[now],l,mid,pos);
        else insert(R[now],mid+1,r,pos);
        }
    int merge(int A,int B) {
        if(!A||!B)return A+B;
        int it=++tot;
        L[it]=merge(L[A],L[B]);
        R[it]=merge(R[A],R[B]);
        return it;
        }
    int query(int now,int l,int r,int nl,int nr) {
        if(!now)return 0;
        if(nl<=l&&r<=nr)return 1;
        if(nl<=mid&&query(L[now],l,mid,nl,nr))return 1;
        if(mid<nr&&query(R[now],mid+1,r,nl,nr))return 1;
        return 0;
        }
    } aux;
int rt[N];
struct SAM {
    int cnt,las;
    int len[N],fa[N],ch[N][26],pos[N];
    void init() {
        cnt=las=1;
        }
    void add(int c,int pi) {
        int p=las,it=las=++cnt;
        len[it]=len[p]+1,pos[it]=pi;
        for(; p&&!ch[p][c]; p=fa[p])ch[p][c]=it;
        if(!p)fa[it]=1;
        else {
            int q=ch[p][c];
            if(len[q]==len[p]+1)fa[it]=q;
            else {
                int np=++cnt;
                for(int i=0; i<26; i++)ch[np][i]=ch[q][i];
                len[np]=len[p]+1,pos[np]=pos[q];
                fa[np]=fa[q],fa[q]=fa[it]=np;
                for(; p&&ch[p][c]==q; p=fa[p])ch[p][c]=np;
                }
            }
        }
    } sett;
void init() {
    sett.init(),aux.init();
    }
int n,tax[N],poi[N];
int dp[N],top[N];
char op[N];
int ans=1;
int main() {
    init();
    scanf("%d",&n);
    scanf("%s",op+1);
    for(int i=1; i<=n; i++) sett.add(op[i]-'a',i),aux.insert(rt[sett.las],1,n,i);
    for(int i=1; i<=sett.cnt; i++)tax[sett.len[i]]++;
    for(int i=1; i<=n; i++)tax[i]+=tax[i-1];
    for(int i=sett.cnt; i>=1; i--)poi[tax[sett.len[i]]--]=i;
    for(int i=sett.cnt; i>1; i--)
rt[sett.fa[poi[i]]]=aux.merge(rt[sett.fa[poi[i]]],rt[poi[i]]); for(int i=2; i<=sett.cnt; i++) { int u=poi[i],ff=sett.fa[u]; if(ff==1)dp[u]=1,top[u]=u; else { if(aux.query(rt[top[ff]],1,n,sett.pos[u]-sett.len[u]+sett.len[top[ff]],sett.pos[u]-1)) dp[u]=dp[ff]+1,top[u]=u,ans=max(ans,dp[u]); else dp[u]=dp[ff],top[u]=top[ff]; } } printf("%d ",ans); } //syktxdy //qy dashenxian //chc duliu
原文地址:https://www.cnblogs.com/stepsys/p/10708983.html