noip2019集训测试赛(一)B.字符串

Description

UPD:本题字符集为全体小写字母

noip2019集训测试赛(一)B.字符串_第1张图片


Input

在这里插入图片描述


Output

在这里插入图片描述


Solution

这题我写了一个查询前暴力get_fail的,复杂度爆炸,但数据水,过了

时间复杂度:O(mlogm)

正解是用所有的s建AC自动机,再建fail树,最后用树状数组维护各种字符串的个数(假的强制在线)。

前序遍历fail树,得到dfn,就可以愉快地维护树状数组查询答案了~

关于fail树可以看看这里:https://blog.csdn.net/niiick/article/details/87947160

tips:把查询的s也插入AC自动机其实对解题没有影响,因为最后统计的实际是每个操作1和操作2对某段区间的贡献,即某段区间的点代表的字符串都包含该串。

但是实际上出题人的意思是通过二进制分组,建立 l o g m logm logm个AC自动机,使用类似启发式合并的方法将size相同的AC自动机合并,这样每个字符串最多被插入 l o g m logm logm次,时间复杂度O(mlogm)。(想想就觉得不会打麻烦)


Code

给的是某位AK比赛的学长的代码,rp++


#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=2000005;
int m,l,tot=1,len[N],bg[N],fail[N],num[N],ch[N][26];
int cnt,idx,nt,head[N],to[N],nxt[N],dfn[N],siz[N],ck[N];
ll c[N],mask,ans,val[N],op[N];
char s[N],str[N];
void adde(int u,int v){
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void insert(char s[],int id){
    int k=1;
    for(int i=0;i<len[id];i++){
        int x=s[i]-'a';
        if(!ch[k][x]){
            ch[k][x]=++tot;
        }
        k=ch[k][x];
    }
    num[id]=k;
}
void build(){
    queue<int> q;
    q.push(1);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            int p=u==1?1:ch[fail[u]][i];
            if(ch[u][i]){
                fail[ch[u][i]]=p;
                q.push(ch[u][i]);
            }else{
                ch[u][i]=p;
            }
        }
    }
    for(int i=2;i<=tot;i++){
        adde(fail[i],i);
    }
}
void dfs(int u){
    dfn[u]=++idx;
    siz[u]=1;
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        dfs(v);
        siz[u]+=siz[v];
    }
}
void add(int i,int v){
    for(;i<=idx;i+=i&(-i)){
        c[i]+=v;
    }
}
ll query(int i){
    ll res=0;
    for(;i;i-=i&(-i)){
        res+=c[i];
    }
    return res;
}
void go(int l,int r){
    nt++;
    int k=1;
    for(int i=l;i<=r;i++){
        int x=s[i]-'a';
        k=ch[k][x];
        if(cxk[k]==nt){//cxk仅仅用于卡常 CXKNMSL(雾
            ans+=val[k];
        }else{
            cxk[k]=nt;
            val[k]=query(dfn[k]);
            ans+=val[k];
        }
    }
}
int main(){
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%lld%s",&op[i],str);
        len[i]=strlen(str);
        bg[i]=l+1;
        insert(str,i);
        for(int j=0;j<len[i];j++){
            s[++l]=str[j];
        }
    }
    build();
    dfs(1);
    for(int i=1;i<=m;i++){
        op[i]^=mask;
        if(op[i]==1){
            add(dfn[num[i]],1);
            add(dfn[num[i]]+siz[num[i]],-1);
        }else if(op[i]==2){
            add(dfn[num[i]],-1);
            add(dfn[num[i]]+siz[num[i]],1);
        }else{
            ans=0;
            go(bg[i],bg[i]+len[i]-1);
            printf("%lld\n",ans);
            mask^=labs(ans);
        }
    }
    return 0;
}

你可能感兴趣的:(AC自动机,字符串)