spoj COT4

终于把坑填了。

题目大意

一开始有两个字符串集合 S,T ,里面都只有一个空串。
5 种操作
1. 在S中选一个字符串 Si ,新建一个字符串 Snew=Si+c , c 是一个小写字母,然后把Snew塞进 S 中。
2. 在T中选一个字符串 Ti ,新建一个字符串 Tnew=Ti+c , c 是一个小写字母,然后把Tnew塞进 T 中。
3. 在T中选一个字符串 Ti ,新建一个字符串 Tnew=c+Ti , c 是一个小写字母,然后把Tnew塞进 T 中。
4. 在T中选两个字符串 Ti,Tj , i 可以等于j.然后把 Ti+Tj 塞入 T 中。
5. 在S中选出 Si , T 中选出Tj.询问 Tj Si 的出现次数。

我的思路

首先要离线。注意到 S 其实就是一棵Trie.我们对这棵 Trie 建出他的后缀数组。

为了方便,我们需要把所有的 Si,Ti 都反序。那么 Trie 上的后缀只有 O(N) 个。

那考虑如何对Trie上的串的反串进行后缀排序。

其实很简单。我们只需要对Trie建出其SAM。然后SAM的fail树就是反串的后缀树。再进行一次DFS就排好序啦!!(说的好简单。。其实不好实现。具体要看代码。。)

既然如此,我们之后考虑的所有的 Suffixj 都是已经排好序的了。

我们对于 T 中所有的字符串Ti维护一个区间 [Li,Ri] ,表示
j[Li,Ri]LCP(Ti,Suffixj)=|Ti| .且这个区间是极大的。我们可能不存在这个区间。计为 [1,1] 即可。

注意到 2,3 操作都只是 4 操作的特例。那我们就只用考虑4操作即可。

假设现在的新串为 p=a+b ,两个区间为 [La,Ra],[Lb,Rb] .假如有一个不存在区间的话,那么这个新串肯定也是不存在的。

由于 a p的前缀。所以 [Lp,Rp][La,Ra] .

要求得 [Lp,Rp] ,其实相当于求出最大的 l 使得Sufl<p,最小的 r 使得Sufr>p.并且这里的 > 是指Sufr的前 |p| 位要比 p 大。然后Lp=l+1,Rp=r1.

那么我们可以二分出 l,r .因为我们已经把后缀排好序了。

当然 l[La,Ra],r[La,Ra]

现在要比较 Sufl p 的大小。因为l[La,Ra],所以 Sufl 的前缀必为 a .那么我们只需要比较Sufl除掉 a 的部分与b的大小就好了。因为一个后缀的后缀也是一个后缀,那我们直接用 Rank[Sufk] Lb,Rb 比较即可。 k l跳了 |a| 步后的后缀,也就相当于在 Trie 上的第 |a| 个祖先。

那么我们就需要一个高效的求第 K 个祖先的做法。

单纯的倍增是O(logN)的。但是常数较大。所以我们需要一个常数小一点的做法。
比如说,把原来的二进制改为一个p进制。为了方便,不妨取 p=32 ,那么我们一个数字 n,n100000 的32进制不会超过4位。

我们存一个数组 Ancp,i,j ,表示 p 这个点向上跳j32i步的祖先是谁。这个可以预处理出来。时间复杂度是 O(N324) .

然后每次询问的时候,我们就把 K 设为p323+p1322+p232+p3.只需要四次迭代就可以计算出第 K 个祖先了!!

有了这个算法之后,我们对于拼接两个字符串并确定出他的区间就有了一个O(logN).

那么我们最后就只需要考虑怎么样统计一个字符串 Ti Sj 中的出现次数了。

因为我们已经知道了 [Li,Ri] ,那么事实上 Ti Sj 的出现次数,相当于 Ti 被多少个 Sj 的后缀覆盖了。那么我们就只需要统计一下
有多少 k[Li,Ri]kAncj .

那我们可以将这个询问挂在 Trie 上的 j 点上,然后边递归边统计就好了。

总的时间复杂度是O(NlogN)了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 300005;

struct TrieInfo
{
    int To[26],Fa[4][32],ref,l;
};

struct Match
{
    int l,r,len;

    Match(void){}
    Match(int a,int b,int c) : l(a),r(b),len(c){}

}Info[MAXN],Map[26];

int Sa[MAXN],Rank[MAXN],Ti[MAXN],Cnt;
int Final[MAXN],Next[MAXN],total,Ans[MAXN];

namespace Trie
{
    const int MAXN = 100005;

    TrieInfo T[MAXN];

    int Trans[MAXN],A[MAXN],cnt;

    void First()
    {
        cnt = 1;
        Trans[1] = -1;
    }

    int Insert(int cur,char c)
    {
        c = c - 'a';
        if (T[cur].To[c]) return T[cur].To[c];
        T[++ cnt].Fa[0][1] = cur;
        T[cnt].l = T[cur].l + 1;
        Trans[cnt] = c;
        return (T[cur].To[c] = cnt);
    }

    int Getfa(int u,int dep)
    {
        int cur = u;
        for(int i = 0;i < 4;i ++,dep >>= 5)
        {
            int p = (dep & 31);
            if (!p) continue;
            cur = T[cur].Fa[i][p];
        }
        return cur;
    }

    void Work()
    {
        for(int i = 2;i < 32;i ++)
            for(int j = 1;j <= cnt;j ++) T[j].Fa[0][i] = T[T[j].Fa[0][i - 1]].Fa[0][1];
        for(int i = 1;i < 4;i ++)
            for(int j = 1;j < 32;j ++)
                for(int k = 1;k <= cnt;k ++)
                {
                    if (j == 1) T[k].Fa[i][j] = T[T[k].Fa[i - 1][31]].Fa[i - 1][1]; else
                        T[k].Fa[i][j] = T[T[k].Fa[i][j - 1]].Fa[i][1];
                }
    }

    void Add(int p,int v)
    {
        for(;p <= Cnt;p += p & -p) A[p] += v;
    }

    int Sum(int p)
    {
        if (p < 0) return 0;
        int tmp = 0;
        for(;p;p -= p & -p) tmp += A[p];
        return tmp;
    }

    int Sum(int l,int r)
    {
        if (l > r) return 0;
        return Sum(r) - Sum(l - 1);
    }

    void Dfs(int Now)
    {
        Add(Rank[Now],1);
        for(int j = Final[Now];j;j = Next[j])
        {
            Ans[j] = Sum(Info[Ti[j]].l,Info[Ti[j]].r);
            if (!Info[Ti[j]].len) Ans[j] = 0;
        }
        for(int i = 0;i < 26;i ++)
            if (T[Now].To[i])
                Dfs(T[Now].To[i]);
        Add(Rank[Now],-1);
    }

    void GetAns()
    {
        Dfs(1);
    }
}

namespace SAM
{
    struct Node
    {
        int To[26],fail,len,down,ref;
    }T[MAXN];

    int Go[MAXN][26];
    int Q[MAXN],Ord[MAXN],S,cnt;

    bool cmp(int a,int b)
    {
        return T[a].len < T[b].len;
    }

    int Add(int Lst,int c,int l,int cur)
    {
        int Nt = ++ cnt,p = Lst;
        T[Nt].len = l;
        for(;p && !T[p].To[c];p = T[p].fail) T[p].To[c] = Nt;
        if (!p) T[Nt].fail = S; else
            if (T[T[p].To[c]].len == T[p].len + 1) T[Nt].fail = T[p].To[c]; else
            {
                int q = ++ cnt,qt = T[p].To[c];
                T[q] = T[qt];
                T[q].len = T[p].len + 1;
                T[qt].fail = T[Nt].fail = q;
                for(;p && T[p].To[c] == qt;p = T[p].fail) T[p].To[c] = q;
            }
        return Nt;
    }

    void Dfs(int Now)
    {
        if (T[Now].ref) Sa[++ Cnt] = T[Now].ref,Rank[T[Now].ref] = Cnt;
        for(int i = 0;i < 26;i ++)
            if (Go[Now][i])
                Dfs(Go[Now][i]);
    }

    void Mark(int tnod,int snod)
    {
        T[snod].down = T[snod].ref = tnod;
        for(int i = 0;i < 26;i ++)
        if (Trie::T[tnod].To[i])
            Mark(Trie::T[tnod].To[i],T[snod].To[i]);
    }

    void Build()
    {
        int fi = 0,en = 1;
        Q[1] = 1;
        S = cnt = 1;
        Trie::T[1].ref = 1;
        T[1].ref = 1;
        while (fi < en)
        {
            int u = Q[++ fi];
            TrieInfo &c = Trie::T[u];
            for(int i = 0;i < 26;i ++)
            if (c.To[i])
            {
                TrieInfo &cur = Trie::T[c.To[i]];
                cur.ref = Add(c.ref,i,cur.l,c.To[i]);
                Q[++ en] = c.To[i];
            }
        }
        Mark(1,1);
        for(int i = 1;i <= cnt;i ++) Ord[i] = i;
        sort(Ord + 1,Ord + cnt + 1,cmp);
        for(;cnt > 1;cnt --)
        {
            int v = Ord[cnt],u = T[v].fail;
            T[u].down = T[v].down;
            TrieInfo &cur = Trie::T[T[v].down];
            int k = Trie::Getfa(T[u].down,cur.l - (T[cur.ref].len - T[u].len));
            Go[u][Trie::Trans[k]] = v;
        }
        Dfs(1);
    }
}

struct Quer
{
    int type,s,t;
    char c;
}Q[MAXN];

int Refer[MAXN],Len[MAXN],N,M,Tc;

inline bool Small(int suf,const Match &a)
{
    return Rank[suf] < a.l;
}

inline bool Large(int suf,const Match &a)
{
    return Rank[suf] > a.r;
}

Match Merge(const Match &a,const Match &b)
{
    if (a.l > a.r) return Match(0,-1,a.len + b.len);
    int l = a.l,r = a.r,l1 = a.l - 1,r1 = a.r + 1,mid;
    for(;l <= r;)
    {
        mid = l + r >> 1;
        if (Small(Trie::Getfa(Sa[mid],a.len),b)) l1 = mid,l = mid + 1; else
            r = mid - 1;
    }
    for(l = a.l,r = a.r;l <= r;)
    {
        mid = l + r >> 1;
        if (Large(Trie::Getfa(Sa[mid],a.len),b)) r1 = mid,r = mid - 1; else
            l = mid + 1;
    }
    return Match(l1 + 1,r1 - 1,a.len + b.len);
}

int main()
{
    Trie::First();
    N = 1;
    Refer[1] = 1;
    scanf("%d", &M);
    for(int i = 1;i <= M;i ++)
    {
        int type,s,t;char c;
        scanf("%d%d", &type, &s);
        if (type == 1)
        {
            scanf(" %c", &c);
            s = Refer[s];
            Refer[++ N] = Trie::Insert(s,c);
        }
        if (type == 2)
        {
            scanf("%d %c", &t, &c);
            Q[i].type = s + 1;Q[i].s = t;Q[i].c = c;
        }
        if (type == 3 || type == 4)
            scanf("%d", &t),Q[i].type = type,Q[i].s = s,Q[i].t = t;
    }
    Trie::Work();
    SAM::Build();
    Tc = 1;
    Info[1].l = 1,Info[1].r = Cnt;
    for(int i = 0;i < 26;i ++)
    {
        int l = 1,r = Cnt,mid;
        Map[i].r = Cnt + 1;
        for(;l <= r;)
        {
            mid = l + r >> 1;
            if (Trie::Trans[Sa[mid]] < i) Map[i].l = mid,l = mid + 1; else
                r = mid - 1;
        }
        for(l = 1,r = Cnt;l <= r;)
        {
            mid = l + r >> 1;
            if (Trie::Trans[Sa[mid]] > i) Map[i].r = mid,r = mid - 1; else
                l = mid + 1;
        }
        Map[i].l ++,Map[i].r --;
        Map[i].len = 1;
    }
    for(int i = 1;i <= M;i ++)
    {
        Quer &c = Q[i];
        if (!c.type) continue;
        if (c.type != 4) ++ Tc;
        if (c.type == 4)
        {
            ++ total;
            c.t = Refer[c.t];
            Next[total] = Final[c.t],Final[c.t] = total;
            Ti[total] = c.s;
        } else
        {
            //We must merge reversely.
            if (c.type == 1) Info[Tc] = Merge(Info[c.s],Map[c.c - 'a']);
            if (c.type == 2) Info[Tc] = Merge(Map[c.c - 'a'],Info[c.s]);
            if (c.type == 3) Info[Tc] = Merge(Info[c.t],Info[c.s]);
        }
    }
    Trie::GetAns();
    for(int i = 1;i <= total;i ++) printf("%d\n", Ans[i]);
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值