生成魔咒-SDOI

题目

题目链接

内容

对于原串的每个前缀,求有多少个不同的子串.

后缀数组

对于长度为 n n 的串,子串个数为 n ( n 1 ) 2 i = 1 n h e i g h t [ i ] \frac{n*(n-1)}{2}-\sum_{i=1}^n height[i]

把字符串翻转,从前往后插入字符,用set查询插入字符的前一个字符pre和后一个字符next

a n s [ i ] = a n s [ i 1 ] + l c p ( p r e , s t r [ i ] ) + l c p ( s t r [ i ] , n e x t ) l c p ( p r e , n e x t ) ans[i]=ans[i-1]+lcp(pre,str[i])+lcp(str[i],next)-lcp(pre,next)

PS: 需要离散化一下

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxbit=20;
struct SuffixArray
{
    int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], st[maxbit][maxn], N;
    bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
    void build(int *r, int n, int m)
    {
        N=n;
        n++;
        int i, j, k=0, p, *x=wa, *y=wb, *t;
        for(i=0;i<m;i++)ws[i]=0;
        for(i=0;i<n;i++)ws[x[i]=r[i]]++;
        for(i=1;i<m;i++)ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
        for(p=j=1;p<n;j<<=1,m=p)
        {
            for(p=0,i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<n;i++)wv[i]=x[y[i]];
            for(i=0;i<m;i++)ws[i]=0;
            for(i=0;i<n;i++)ws[wv[i]]++;
            for(i=1;i<m;i++)ws[i]+=ws[i-1];
            for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
        
        for(i=0;i<n;i++)rank[sa[i]]=i;
        
        for(i=0;i<n-1;height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    }
    void build_st()     //st表
    {
        int i, k;
        for(i=1;i<=N;i++)st[0][i]=height[i];
        for(k=1;k<=maxbit;k++)
            for(i=1;i+(1<<k)-1<=N;i++)
                st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
    }
    int lcp(int x, int y)   //最长公共前缀
    {
        int l=x, r=y;
        if(l>r)swap(l,r);
        if(l==r)return N-sa[l];
        int t=log2(r-l);
        return min(st[t][l+1],st[t][r-(1<<t)+1]);
    }
}SA;
int Hash[maxn],num,strcp[maxn],a[maxn];
int get_hash(int x)
{
    return lower_bound(Hash+1,Hash+num,x)-Hash;
}
set<int> s;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    int n;
    cin>>n;
    rep(i,0,n-1) 
    {
        cin>>a[i];
        Hash[i+1]=a[i];
    }
    rep(i,0,n-1) strcp[i]=a[n-1-i];
    sort(Hash+1,Hash+n+1);
    num=unique(Hash+1,Hash+n+1)-Hash;
    rep(i,0,n-1) strcp[i]=get_hash(strcp[i]);
    strcp[n]=0;
    SA.build(strcp,n,n+100);
    SA.build_st();
    ll sum=0,now=1;
    per(i,n-1,0)
    {
        auto it=s.lower_bound(SA.rank[i]);
        int l,r;
        if(it==s.end()) r=0;
        else r=*it;
        if(it==s.begin()) l=0;
        else
        {
            it--;
            l=*it;
        }
        if(l&&r) sum-=SA.lcp(l,r);
        if(l) sum+=SA.lcp(l,SA.rank[i]);
        if(r) sum+=SA.lcp(r,SA.rank[i]);
        s.insert(SA.rank[i]);
        cout<<now*(now+1)/2-sum<<'\n';
        now++;
    }
    return 0;
}

后缀自动机

SAM裸题

由于字符集过大,要用map维护

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define debug(x) cerr<<#x<<' '<<x<<'\n'
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
ll ans=0;
struct sam {
    map<int,int> t[maxn<<1];
    int len[maxn<<1] = {-1}, fa[maxn<<1], sz = 2, last = 1;
    void ins(int ch) {
        int p = last, np = last = sz++; 
        len[np] = len[p] + 1;
        for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np;
        if (!p) { 
            fa[np] = 1; 
            ans+=len[np]-len[fa[np]];
            return; 
        }
        int q = t[p][ch];
        if (len[p] + 1 == len[q]) fa[np] = q;
        else {
            int nq = sz++; len[nq] = len[p] + 1;
            ans-=len[q]-len[fa[q]];
            t[nq]=t[q];
            fa[nq] = fa[q];
            fa[np] = fa[q] = nq;
            ans+=len[nq]-len[fa[nq]];
            ans+=len[q]-len[fa[q]];
            for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq;
        }
        ans+=len[np]-len[fa[np]];
    }
}sam;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    int n;
    cin>>n;
    rep(i,1,n) {
        int x;
        cin>>x;
        sam.ins(x);
        cout<<ans<<'\n';
    }
    return 0;
}

发布了10 篇原创文章 · 获赞 0 · 访问量 324

猜你喜欢

转载自blog.csdn.net/WA_King/article/details/104092518
今日推荐