Loading

P5212 SubString

大意

给你一个初始字符串。现在需要你实现两种操作,第一种是在字符串最后插入字符,第二种是给出一个字符串,求这个字符串在整个字符串中出现的次数。强制在线。

Sol

牛逼题啊。首先对于动态插入字符的问题,别的算法肯定是行不通的(可能哈希可以搏一搏),所以我们考虑十分贴合这题的 SAM。SAM 本身就是采用增量法每次 \(O(1)\) 构造的。这样就轻松解决了插入字符的问题。

好接下来考虑这个查询。我们发现,对于求子串出现次数的做法,似乎只能是在 SAM 构造的 Parent Tree 上对每个节点子树求和,这个做法非常显然,因为子串出现次数实际上就是 endpos 集合的大小,具体可以见我的博客

但是不难发现,这样的方法在强制在线的情况下会寄掉,因为我们需要在建好 SAM 之后跑一次大法师。所以,我们需要考虑一个更加高妙的想法。

思考我们在构造 Parent Tree 的时候,会进行什么操作?断一条边,连一条边,然后询问子树中权值和(因为在 SAM 中复制的节点不对父亲的 endpos 大小产生影响)。断边连边的操作容易使我们想到直接无脑用维护子树的 LCT 来做。这样 SAM 插入一次就是 \(O(\log n)\) 的了。然后最终复杂度是 \(O(n\log n)\),卡卡就过了?

对于具体实现,我们考虑 Parent Tree 是有一个固定的点的,所以完全不需要 makeroot 操作(而且也不能,因为这样一来就影响了子树的问题)。而且,我们在实现 LCT 的时候,完全不需要用子树求和的,而是可以考虑每个节点的权值对其它节点的影响。还句话说,我们可以考虑某个节点的权值如果是 \(1\),那么它会对它的所有祖先产生贡献,那就变成了链上信息维护,普通 LCT 就可以了。

Code

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e6+10;
struct LCT{
    struct node{int ch[2],fa,val,tag;}tr[MAXN];
    #define ls tr[x].ch[0]
    #define rs tr[x].ch[1]
    bool nroot(int x){return tr[tr[x].fa].ch[0]==x||tr[tr[x].fa].ch[1]==x;}
    void add(int x,int v){tr[x].val+=v,tr[x].tag+=v;}
    void pushdown(int x){
        if(!tr[x].tag) return;
        if(ls) add(ls,tr[x].tag);
        if(rs) add(rs,tr[x].tag);
        tr[x].tag=0;
    }
    void rot(int x){
        int f=tr[x].fa,k=(x==tr[f].ch[1]),g=tr[f].fa,v=tr[x].ch[k^1];
        if(nroot(f)) tr[g].ch[f==tr[g].ch[1]]=x;
        tr[x].ch[k^1]=f;tr[f].ch[k]=v;
        if(v) tr[v].fa=f;
        tr[f].fa=x;tr[x].fa=g;
    }int stk[MAXN],top;
    void splay(int x){
        int tmp=x;top=0;stk[++top]=tmp;
        while(nroot(tmp)) tmp=tr[tmp].fa,stk[++top]=tmp;
        while(top) pushdown(stk[top--]);
        while(nroot(x)){
            int f=tr[x].fa,g=tr[f].fa;
            if(nroot(f))
                rot((f==tr[g].ch[1])^(x==tr[f].ch[1])?x:f);
            rot(x);
        }
    }
    void access(int x){for(int s=0;x;s=x,x=tr[x].fa)splay(x),rs=s;}
    void link(int x,int y){
        tr[x].fa=y;access(y);splay(y);
        add(y,tr[x].val);
    }
    void cut(int x){
        access(x);splay(x);
        add(ls,-tr[x].val);tr[ls].fa=0;ls=0;
    }
}T;
struct SAM{int fa,len,ch[2];}tr[MAXN];
int tot=1,lst=1;
void insert(int c){
    int np=++tot,p=lst;lst=np;
    tr[np].len=tr[p].len+1;T.tr[np].val=1;
    while(p&&!tr[p].ch[c]) tr[p].ch[c]=np,p=tr[p].fa;
    if(!p) T.link(np,1),tr[np].fa=1;
    else{
        int v=tr[p].ch[c];
        if(tr[v].len==tr[p].len+1)
            T.link(np,v),tr[np].fa=v;
        else{
            int nv=++tot;tr[nv]=tr[v];
            T.link(nv,tr[nv].fa);
            tr[nv].len=tr[p].len+1;
            while(p&&tr[p].ch[c]==v) tr[p].ch[c]=nv,p=tr[p].fa;
            T.cut(v);
            tr[v].fa=tr[np].fa=nv;
            T.link(v,nv);T.link(np,nv);
        }
    }
}
string decodeWithMask(string s, int mask) {
	for (int j = 0; j < (int)s.length(); j++) {
		mask = (mask * 131 + j) % s.length();
		
		char t = s[j];
		s[j] = s[mask];
		s[mask] = t;
	}
	
	return s;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int Q;cin>>Q;
    string s;cin>>s;
    rep(i,0,(int)s.size()-1)
        insert(s[i]-'A');
    string op;
    int mask=0;
    while(Q--){
        cin>>op>>s;
        s=decodeWithMask(s,mask);
        if(op[0]=='A'){
            rep(i,0,(int)s.size()-1)
                insert(s[i]-'A');
        }
        else{
            int pt=0,p=1;
            while(pt<(int)s.size()&&tr[p].ch[s[pt]-'A'])
                p=tr[p].ch[s[pt++]-'A'];
            if(pt==(int)s.size()){
                T.splay(p);
                int res=T.tr[p].val;
                cout<<res<<'\n';mask^=res;
            }else cout<<0<<'\n';
        }
    }
    return 0;
}
posted @ 2022-04-23 09:21  ZCETHAN  阅读(24)  评论(0编辑  收藏  举报