后缀自动机小专题

菜鸡万年更博。

全是菜题。

 

SP1811 LCS - Longest Common Substring

对一个串建立自动机,另一个串在自动机上遍历,失配时跳father更新即可。ans=遍历到的点的len的最大值。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=25e4+3;

char S[N],T[N];

struct SAM
{
    int ch[26],fa,len;
}sam[N<<1];

int tot=1,lst=1;
void insert(int x)
{
    int p=lst;lst=++tot;sam[lst].len=sam[p].len+1;
    for(;p&&!sam[p].ch[x];p=sam[p].fa) sam[p].ch[x]=lst;
    if(!p) sam[lst].fa=1;
    else
    {
        int q=sam[p].ch[x];
        if(sam[p].len+1==sam[q].len) sam[lst].fa=q;
        else
        {
            int nq=++tot;sam[nq]=sam[q],sam[nq].len=sam[p].len+1;
            sam[lst].fa=sam[q].fa=nq;
            for(;sam[p].ch[x]==q;p=sam[p].fa) sam[p].ch[x]=nq;
        }
    }
}

int main()
{
    scanf("%s%s",S+1,T+1);
    for(int i=1;S[i];++i) insert(S[i]-'a');
    int now=1,len=0,Ans=0;
    for(int i=1;T[i];++i)
    {
        T[i]-='a';
        if(sam[now].ch[T[i]]) ++len,now=sam[now].ch[T[i]];
        else
        {
            while(now&&!sam[now].ch[T[i]]) now=sam[now].fa;
            if(!now) now=1,len=0;
            else len=sam[now].len+1,now=sam[now].ch[T[i]];
        }
        Ans=max(Ans,len);
    }
    cout<<Ans;
    return 0;
}
View Code

SP1812 LCS2 - Longest Common Substring II

可以跟上题一样做,也可以建个广义后缀自动机。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e5+5;

int T,n;
char s[N>>1];

int ch[N][26],len[N],fa[N];

int tot=1,lst=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q];
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            fa[q]=fa[lst]=nq;
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int cnt[N],sa[N];
int mx[N],mn[N];
int main()
{
    scanf("%s",s+1);int sl=strlen(s+1);
    lst=tot=1;
    for(int i=1;i<=sl;++i) insert(s[i]-'a');
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=sl;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i;
    memset(mn,0x3f,sizeof(mn));
    while(~scanf("%s",s+1))
    {
        int now=1,L=0,x;
        for(int i=1;s[i];++i)
        {
            x=s[i]-'a';
            while(now&&!ch[now][x]) now=fa[now],L=len[now];
            if(!now) now=1,L=0;
            else ++L,now=ch[now][x],mx[now]=max(mx[now],L);
        }
        for(int i=tot,u,f;i;--i)
        {
            u=sa[i],f=fa[u];
            mx[f]=max(mx[f],min(mx[u],len[f]));
            mn[u]=min(mn[u],mx[u]);mx[u]=0;
        }
    }
    int Ans=0;
    for(int i=1;i<=tot;++i) Ans=max(Ans,mn[i]);
    cout<<Ans<<'
';
    return 0;
}
View Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
#include<bitset>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e6+5;

int id=-1;
char s[100003];

struct SAM
{
    int ch[26],fa,len;
    bitset<10> bst;
}sam[N];

int lst,tot=1;
int newnode(int p,int q,int x)
{
    int nq=++tot;sam[nq]=sam[q];sam[nq].len=sam[p].len+1;
    sam[q].fa=nq;
    for(;p&&sam[p].ch[x]==q;p=sam[p].fa) sam[p].ch[x]=nq;
    return nq;
}

int insert(int p,int x)
{
    if(sam[p].ch[x])
    {
        int q=sam[p].ch[x];
        if(sam[p].len+1==sam[q].len)
        {
            sam[q].bst[id]=1;
            return q;
        }
        else
        {
            int nq=newnode(p,q,x);
            sam[nq].bst[id]=1;
            return nq;
        }
    }
    else
    {
        int np=++tot;sam[np].len=sam[p].len+1;sam[np].bst[id]=1;
        for(;p&&!sam[p].ch[x];p=sam[p].fa) sam[p].ch[x]=np;
        if(!p) sam[np].fa=1;
        else
        {
            int q=sam[p].ch[x];
            if(sam[p].len+1==sam[q].len) sam[np].fa=q;
            else sam[np].fa=newnode(p,q,x);
        }
        return np;
    }
}

void debug()
{
    for(int i=1;i<=tot;++i)
    {
        for(int j=0;j<10;++j) cout<<sam[i].bst[j]<<' ';
        puts("");
    }
}

int cnt[N],sa[N];
int main()
{
    while(~scanf("%s",s+1))
    {
        lst=1;++id;
        for(int i=1;s[i];++i) lst=insert(lst,s[i]-'a');
    }
    for(int i=1;i<=tot;++i) ++cnt[sam[i].len];
    for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[sam[i].len]--]=i;
    int Ans=0;++id;
    for(int i=tot;i>1;--i)
    {
        if(sam[sa[i]].bst.count()==(unsigned)id) Ans=max(Ans,sam[sa[i]].len);
        sam[sam[sa[i]].fa].bst|=sam[sa[i]].bst;
    }
    cout<<Ans;
    return 0;
}
广义后缀自动机

SP10570 LONGCS - Longest Common Substring

三倍经验

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e4+5;

int T,n;
char s[N>>1];

int ch[N][26],len[N],fa[N];

int tot=1,lst=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q];
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            fa[q]=fa[lst]=nq;
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int cnt[N],sa[N];
int mx[N],mn[N];
int main()
{
    T=read();
    while(T--)
    {
        n=read();scanf("%s",s+1);int sl=strlen(s+1);
        lst=tot=1;
        memset(ch,0,sizeof(ch));
        for(int i=1;i<=sl;++i) insert(s[i]-'a');
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=tot;++i) ++cnt[len[i]];
        for(int i=1;i<=sl;++i) cnt[i]+=cnt[i-1];
        for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i;
        memset(mn,0x3f,sizeof(mn));
        for(int a=2;a<=n;++a)
        {
            scanf("%s",s+1);
            int now=1,L=0,x;
            for(int i=1;s[i];++i)
            {
                x=s[i]-'a';
                while(now&&!ch[now][x]) now=fa[now],L=len[now];
                if(!now) now=1,L=0;
                else ++L,now=ch[now][x],mx[now]=max(mx[now],L);
            }
            for(int i=tot,u,f;i;--i)
            {
                u=sa[i],f=fa[u];
                mx[f]=max(mx[f],min(mx[u],len[f]));
                mn[u]=min(mn[u],mx[u]);mx[u]=0;
            }
        }
        int Ans=0;
        for(int i=1;i<=tot;++i) Ans=max(Ans,mn[i]);
        cout<<Ans<<'
';
    }
    return 0;
}
View Code

P3181 [HAOI2016]找相同字符

求出每个子串在两个串中的出现次数,乘上每个点代表的不同子串个数len[p]-len[fa[p]]即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=8e5+5;

char S[N>>2],T[N>>2];

int id;
int ch[N][26],len[N],fa[N],siz[2][N];

int lst=1,tot=1;
int newnode(int p,int x)
{
    int q=ch[p][x],nq=++tot;len[nq]=len[p]+1;
    fa[nq]=fa[q],fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq]));
    for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
    return nq;
}

int insert(int p,int x)
{
    if(ch[p][x])
    {
        int q=ch[p][x];
        if(len[p]+1==len[q])
        {
            siz[id][q]=1;
            return q;
        }
        else
        {
            int nq=newnode(p,x);
            siz[id][nq]=1;
            return nq;
        }
    }
    else
    {
        int np=++tot;len[np]=len[p]+1,siz[id][np]=1;
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) fa[np]=1;
        else
        {
            int q=ch[p][x];
            if(len[p]+1==len[q]) fa[np]=q;
            else fa[np]=newnode(p,x);
        }
        return np;
    }
}

int cnt[N],sa[N];
int main()
{
    scanf("%s%s",S+1,T+1);
    lst=1,id=0;
    for(int i=1;S[i];++i)
        lst=insert(lst,S[i]-'a');
    lst=id=1;
    for(int i=1;T[i];++i)
        lst=insert(lst,T[i]-'a');
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[len[i]]--]=i;
    LL ans=0;
    for(int i=tot;i>1;--i)
    {
        siz[0][fa[sa[i]]]+=siz[0][sa[i]],siz[1][fa[sa[i]]]+=siz[1][sa[i]];
        ans+=1ll*siz[0][sa[i]]*siz[1][sa[i]]*(len[sa[i]]-len[fa[sa[i]]]);
    }
    cout<<ans;
    return 0;
}
View Code

P3975 [TJOI2015]弦论

求出每个点后有多少串,然后按照字典序像splay一样二分出来就就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=1e6+5;

int t,k;
char s[N>>1];

int ch[N][26],len[N],fa[N],siz[N];

int tot=1,lst=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q],siz[nq]=0;
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            fa[q]=fa[lst]=nq;
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int f[N];
int cnt[N],sa[N];
int main()
{
    scanf("%s",s+1);int n=strlen(s+1);
    t=read(),k=read();
    for(int i=1;i<=n;++i) insert(s[i]-'a');
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i;
    for(int i=tot;i;--i)
    {
        if(!t) siz[sa[i]]=1;
        else siz[fa[sa[i]]]+=siz[sa[i]];
    }
    siz[0]=siz[1]=0;
    for(int i=tot;i;--i)
    {
        f[sa[i]]=siz[sa[i]];
        for(int j=0;j<26;++j) f[sa[i]]+=f[ch[sa[i]][j]];
    }
    if(f[1]<k){puts("-1");return 0;}
    int now=1;
    while(k>siz[now])
    {
        k-=siz[now];
        for(int i=0;i<26;++i)
        {
            if(k>f[ch[now][i]]) k-=f[ch[now][i]];
            else{putchar(i+'a');now=ch[now][i];break;}
        }
    }
    return 0;
}
View Code

SP7258 SUBLEX - Lexicographical Substring Search

跟上个一样的题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e5+5;

int T,k;
char s[N>>1];

int ch[N][26],fa[N],len[N];

int lst=1,tot=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1;
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            fa[lst]=fa[q]=nq;
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int f[N],cnt[N],sa[N];
int main()
{
    scanf("%s",s+1);
    for(int i=1;s[i];++i) insert(s[i]-'a');
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[len[i]]--]=i;
    for(int i=tot;i;--i)
    {
        f[sa[i]]=1;
        for(int j=0;j<26;++j) f[sa[i]]+=f[ch[sa[i]][j]];
    }
    T=read();
    while(T--)
    {
        k=read();
        int now=1;
        while(k)
        {
            for(int i=0;i<26;++i)
            {
                if(k>f[ch[now][i]]) k-=f[ch[now][i]];
                else{putchar(i+'a');now=ch[now][i];--k;break;}
            }
        }
        puts("");
    }
    return 0;
}
View Code

P4070 [SDOI2016]生成魔咒

“每个点代表的不同子串个数是len[p]-len[fa[p]]”,每次插入ans加上len[lst]-len[fa[lst]]即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e5+5;

int n;LL ans=0;
int a[N];

map<int,int> ch[N];int len[N],fa[N];

int tot=1,lst=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[q]=fa[lst]=nq;ch[nq]=ch[q];
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
    ans+=len[lst]-len[fa[lst]];
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i)
    {
        insert(read());
        cout<<ans<<'
';
    }
    return 0;
}
View Code

BZOJ 3277 串

建广义后缀自动机,求出每个子串出现在几个串中看是否能产生贡献。每个点加上父亲的贡献,因为父亲一定会在这儿出现。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=2e5+5;

int n,k;
char s[N];
string S[N];

int ch[N][2],fa[N],len[N];

int tot=1,lst;
void insert(int p,int x)
{
    lst=++tot;len[lst]=len[p]+1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq]));
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int vis[N],tims[N];
void solve(string S,int x)
{
    int now=1;int n=S.length();
    for(int i=0;i<n;++i)
    {
        now=ch[now][S[i]-'a'];
        for(int p=now;p&&vis[p]!=x;p=fa[p]) vis[p]=x,++tims[p];
    }
}

int sum[N];
void query(string S)
{
    int n=S.length();int ans=0;
    int now=1;
    for(int i=0;i<n;++i)
    {
        now=ch[now][S[i]-'a'];
        ans+=sum[now];
    }
    cout<<ans<<' ';
}

int cnt[N],sa[N];
int main()
{
    n=read(),k=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        S[i]=string(s+1);
        int now=1;lst=1;
        for(int j=1;s[j];++j) insert(now,s[j]-='a'),now=ch[now][s[j]];
    }
    for(int i=1;i<=n;++i) solve(S[i],i);
    for(int i=1;i<=tot;++i)    sum[i]=(tims[i]>=k)*(len[i]-len[fa[i]]);
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i;
    for(int i=1;i<=tot;++i) sum[sa[i]]+=sum[fa[sa[i]]];
    for(int i=1;i<=n;++i) query(S[i]);
    return 0;
}
View Code

P4248 [AHOI2013]差异

把串倒着插入,两个子串的lcp长度就是他们在parent树上的祖先的长度。

记录每个点子树的后缀个数,计算与它父亲的其他儿子产生的贡献=siz[p]*siz[fa[p]]*len[fa[p]]。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=1e6+5;

char s[N>>1];

int ch[N][26],len[N],fa[N],siz[N];

int lst=1,tot=1;
void insert(int x)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[lst]=fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[q]));
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int sa[N],cnt[N];
int main()
{
    scanf("%s",s+1);int n=strlen(s+1);
    for(int i=n;i;--i) insert(s[i]-'a');
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[len[i]]--]=i;
    LL ans=0;
    for(int i=tot;i>1;--i)
    {
        ans+=1ll*siz[sa[i]]*siz[fa[sa[i]]]*len[fa[sa[i]]];
        siz[fa[sa[i]]]+=siz[sa[i]];
    }
    cout<<1ll*(n-1)*n*(n+1)/2-2*ans;
    return 0;
}
View Code

P2178 [NOI2015]品酒大会

差不多和上个题一样。

两个r相似的串可以看做是从p,q开始的两个后缀的lcp长度为r。

最初ans1[r]求的是lcp恰好长为r的个数,ans2[r]求的是恰好为r的最大值。

最后再求个后缀和和后缀max就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=6e5+5;
const LL INF=0x3f3f3f3f3f3f3f3f;

int n,a[N>>1];
char s[N>>1];

int ch[N][26],len[N],fa[N],siz[N];
LL mx[N],mn[N];

int lst=1,tot=1;
void insert(int x,int v)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1,mx[lst]=mn[lst]=v;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;len[nq]=len[p]+1,fa[nq]=fa[q];
            fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq]));
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

LL ans1[N],ans2[N];
int sa[N],cnt[N];
int main()
{
    memset(ans2,-0x3f,sizeof(ans2));
    memset(mx,-0x3f,sizeof(mx));memset(mn,0x3f,sizeof(mn));
    n=read();scanf("%s",s+1);
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=n;i;--i) insert(s[i]-'a',a[i]);
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[len[i]]--]=i;
    for(int i=tot;i;--i)
    {
        ans1[len[fa[sa[i]]]]+=1ll*siz[fa[sa[i]]]*siz[sa[i]];
        if(mx[sa[i]]!=-INF&&mn[sa[i]]!=INF&&mx[fa[sa[i]]]!=-INF&&mn[fa[sa[i]]]!=INF)
        {
            ans2[len[fa[sa[i]]]]=max(ans2[len[fa[sa[i]]]],1ll*mx[sa[i]]*mx[fa[sa[i]]]);
            ans2[len[fa[sa[i]]]]=max(ans2[len[fa[sa[i]]]],1ll*mn[sa[i]]*mn[fa[sa[i]]]);
        }
        siz[fa[sa[i]]]+=siz[sa[i]];
        mx[fa[sa[i]]]=max(mx[fa[sa[i]]],mx[sa[i]]);
        mn[fa[sa[i]]]=min(mn[fa[sa[i]]],mn[sa[i]]);
    }
    for(int i=n-2;~i;--i) ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
    for(int i=0;i<n;++i) cout<<ans1[i]<<' '<<(ans1[i]?ans2[i]:0)<<'
';
    return 0;
}
View Code

CF873F Forbidden Indices

被禁止就当做没出现,否则当做出现。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=4e5+5;

int n;
char s[N>>1],t[N>>1];

int ch[N][26],len[N],fa[N];
int siz[N];

int tot=1,lst=1;
void insert(int x,bool flag)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=flag;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;len[nq]=len[p]+1,fa[nq]=fa[q];
            fa[lst]=fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[q]));
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

int cnt[N],sa[N];
int main()
{
    n=read();scanf("%s%s",s+1,t+1);
    for(int i=1;i<=n;++i) insert(s[i]-'a',(t[i]!='1'));
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=tot;i;--i) sa[cnt[len[i]]--]=i;
    for(int i=tot;i;--i) siz[fa[sa[i]]]+=siz[sa[i]];
    LL ans=0;
    for(int i=1;i<=tot;++i)    ans=max(ans,1ll*len[i]*siz[i]);
    cout<<ans;
    return 0;
}
View Code

CodeChef Killjee and k-th letter

做不下去了, 还没有看懂它建后缀树的地方,等待zbq解答ing..

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

inline LL read()
{
    char c=getchar();LL num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=c=='-'?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num*f;
}

const int N=4e5+5;

int n;LL sum[N];
char s[N>>1];

int son[N][26];
int ch[N][26],len[N],fa[N],rig[N],pos[N];

int lst=1,tot=1;
void insert(int x,int i)
{
    int p=lst;lst=++tot;len[lst]=len[p]+1;++rig[lst],pos[lst]=i;
    for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst;
    if(!p) fa[lst]=1;
    else
    {
        int q=ch[p][x];
        if(len[p]+1==len[q]) fa[lst]=q;
        else
        {
            int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq]));
            for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }
}

void prework()
{
    static int cnt[N],sa[N];
    for(int i=1;i<=tot;++i) ++cnt[len[i]];
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i;
    for(int i=tot,x,y;i;--i)
    {
        x=sa[i],y=fa[sa[i]];
        rig[y]+=rig[x],pos[y]=pos[x];
        son[y][s[pos[x]-len[y]]-'a']=x;
    }
}

int dfn[N],bound;
void dfs(int u)
{
    dfn[++bound]=u;
    for(int i=0;i<26;++i)
        if(son[u][i]) dfs(son[u][i]);
}

LL calc(int l,int r)
{
    return 1ll*(l+r)*(r-l+1)/2;
}

int query(LL k)
{
    int l=1,r=tot,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(sum[mid]<k) l=mid+1;
        else r=mid-1;
    }
    k-=sum[l-1];int t=dfn[l],h=len[fa[t]]+1;l=h,r=len[t];
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(1ll*rig[t]*calc(h,mid)<k) l=mid+1;
        else r=mid-1;
    }
    k-=1ll*rig[t]*calc(h,l-1);
    k=(k-1)%l+1;
    return s[pos[t]-k+1];
}

int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    reverse(s+1,s+n+1);
    for(int i=1;i<=n;++i) insert(s[i]-'a',i);
    prework();dfs(1);
    for(int i=1;i<=bound;++i) sum[i]=sum[i-1]+1ll*calc(len[fa[dfn[i]]]+1,len[dfn[i]])*rig[dfn[i]];
    int Q=read(),G=0;
    while(Q--)
    {
        int p,ans;LL m;p=read(),m=read();
        LL k=1ll*p*G%m+1;
        G+=(ans=query(k));
        cout<<(char)ans<<'
';
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lovewhy/p/10704390.html