BZOJ 2865 字符串识别

来源:互联网 发布:oute66手机导航软件 编辑:程序博客网 时间:2024/06/01 10:27

后缀自动机+线段树

考虑最终包含点i的仅出现一次的子串长什么样。记这个子串是[l,r]。

l < i < r : 即i不碰到左右端点,对于这种情况,可以暴力在SAM上找出所有出现一次的串,用线段树更新答案。

l = i ≤ r : 设以r为右端点的出现一次的最短的串为[l’,r],显然要有l≤l’。于是依然可以暴力找出所有出现一次的串,用r来更新[1,l-1]的答案。

l ≤ i = r : 设以r为右端点的出现一次的最短的串为[l’,r],显然要有l=l’,所以这种情况等价于第一种。

所以只需要两棵线段树。本题用SAM会被卡内存,SAM的大小适当开小一点即可。。。

#include<cstdio>#include<cstring>#define N 500003#define M 850003#define A 26#define cmin(u,v) (u)>(v)?(u)=(v):0using namespace std;namespace runzhe2000{    const int INF = 1<<29;    int n, sum[N], ans[N];    char s[N];    struct SAM    {        SAM *next[A], *fail;        int len, right;    }mem[M], *tot, *null, *root, *last, *pos[M];    SAM* newnode(int len)    {        SAM *p = ++tot;        *p = *null; p->len = len;        return p;    }    void init()    {        null = tot = mem;        for(int i = 0; i < A; i++) null->next[i] = null;        null->fail = null; null->len = null->right = 0;        last = root = newnode(0);    }    void insert(int v)    {        SAM *np = newnode(last->len + 1), *p = last; last = np; np->right = 1;        for(; p != null && p -> next[v] == null; p = p->fail) p->next[v] = np;        if(p == null) np->fail = root;        else        {            SAM *q = p->next[v];            if(p->len + 1 == q->len) np->fail = q;            else            {                SAM *nq = newnode(0);                *nq = *q; nq->len = p->len + 1;                nq -> right = 0; // attention                q->fail = np->fail = nq;                for(; p != null && p->next[v] == q; p = p->fail) p->next[v] = nq;            }        }    }    struct segment_tree    {        struct node {int val;}t[N*5];        void build(int x, int l, int r)        {            t[x].val = INF; if(l == r)return;            int mid = (l+r)>>1;            build(x<<1,l,mid); build(x<<1|1,mid+1,r);        }        void modi(int x, int l, int r, int ql, int qr, int val)        {            if(ql <= l && r <= qr) {cmin(t[x].val, val); return;}            int mid = (l+r)>>1;            if(ql <= mid) modi(x<<1,l,mid,ql,qr,val);            if(mid < qr) modi(x<<1|1,mid+1,r,ql,qr,val);        }        void pushall(int x, int l, int r, int val)        {            cmin(val, t[x].val);            if(l == r){cmin(ans[l], val); return;}            int mid = (l+r)>>1;            pushall(x<<1,l,mid,val);            pushall(x<<1|1,mid+1,r,val);        }    }t1,t2;    void main()    {        scanf("%s",s+1);        n = strlen(s+1);        init();        for(int i = 1; i <= n; i++)            insert(s[i] - 'a');        t1.build(1,1,n);         t2.build(1,1,n);        int cnt = tot - mem;        for(SAM *i = tot; i != mem; i--) sum[i->len]++;        for(int i = 1; i <= n; i++) sum[i] += sum[i-1];        for(SAM *i = tot; i != mem; i--) pos[sum[i->len]--] = i;        for(int i = cnt; i; i--)         {            SAM *p = pos[i]; if(p == root) continue;            p->fail->right += p->right;            if(p->right == 1)            {                int l = p->len - p->fail->len , r = p->len;                if(1 <= l-1)t1.modi(1,1,n,1,l-1,r);                t2.modi(1,1,n,l,r,r-l+1);            }        }        memset(ans,63,sizeof(ans));        t1.pushall(1,1,n,INF);        for(int i = 1; i <= n; i++) ans[i] = ans[i] - i + 1;        t2.pushall(1,1,n,INF);        for(int i = 1; i <= n; i++) printf("%d\n",ans[i]);    }}int main(){    runzhe2000::main();}
0 0