Codeforces Round #305 (Div. 1)E. Mike and Friends(AC自动机+fail树dfs序+主席树)

10 篇文章 1 订阅
6 篇文章 0 订阅

传送门

题意:给定n个字符串,q次询问,每次给出正整数L,R,X,要求输出第x个字符串在第l个字符串到第r个字符串的总匹配次数。
(字符串长度之和不大于2e5)

分析:

  • 首先我们考虑对于一个给定的字符串集合,我们如何求出其中某个字符串在集合中的匹配次数?比如给定集合cacaa,baabaa,aa。求出aa在集合中的总匹配次数。(易知答案是4) 。
  • 我们可以把所有的串插入trie树中,每插入一个字符,就给trie树相应节点val值+1,设aa尾节点为endstr[k],插入完成后val[endstr[k]]的值就是以aa为前缀的字符串个数 。我们再通过fail树,求出以endstr[k]为根的子树的点权和,二者相加就是答案了。(因为在fail树上,根节点是其子树节点的后缀,并且fail树根节点的后代节点 等价于 所有的以根节点为后缀的字符串尾节点,如下图中,aa在fail树的后代有baa,baabaa,cacaa,1+3=4)

在这里插入图片描述

  • 搞懂如何在字符串集合中求匹配次数问题后,这题算是解出一半了。剩下的难点在于如何建主席树?

  • 首先我们利用dfs序把子树求和变成了区间求和,用线段树维护区间和。

  • 题目每次要求是在[l,r]字符串集合中,求x串的匹配次数,x串未必是[l,r]当中的一个。所以我们首先把1~n所有串插入AC自动机,建fail树,求出dfs序。然后每次询问我们只需要求出[l,r]字符串匹配后,val[endstr[x]]的子树和。也就是[1,r]的贡献减去[1,l-1]的贡献。

  • 建树:枚举每个串,串i每个节点都需要在dfs序相应位置上更新一次,最后保留更新完毕的主席树版本p[i]。 p[r]-p[l-1]这棵树便是只考虑[l,r]串进行匹配后的贡献。 查询[in[endstr[x]], out[endstr[x]]]区间和即可。

  • 其实这里建主席树难点在于,我们维护的是dfs序的区间和,但用于版本更新的每个串的每个字符。

详见注释。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e5+10;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
int tree[maxn][26];
int fail[maxn];
int tot=0;//ac自动机结点数
queue<int> q;
int endstr[maxn];//尾节点编号
vector<int> sid[maxn];//每个字符串 abaccd -> 010223
vector<int> ft[maxn];//建fail树
int len[maxn];//每个字符串的长度
inline void insert(char *s,int x)
{
    len[x]=strlen(s);
    int rt=0;
    for(int i=0;i<len[x];i++)
    {   
        int id=s[i]-'a';
        sid[x].push_back(id);
        if(!tree[rt][id]) tree[rt][id]=++tot;
        rt=tree[rt][id];
    }
    endstr[x]=rt;
}
inline void getfail()
{
    for(int i=0;i<26;i++)
    {
        if(tree[0][i]) 
        {
            q.push(tree[0][i]);
        }
    }
    while(q.size()) 
    {
        int f=q.front();
        q.pop();
        ft[fail[f]].push_back(f);
        for(int i=0;i<26;i++)
        {
            int u=tree[f][i];
            if(!u) 
            {
                tree[f][i]=tree[fail[f]][i];
            }
            else 
            {
                fail[u]=tree[fail[f]][i];
                q.push(u);
            }
        }
    }
}
int in[maxn],out[maxn],dfsclock;//dfs序 子树求合转化成区间求合
inline void dfs(int rt)
{
    in[rt]=++dfsclock;
    for(auto i:ft[rt]) 
    {
        dfs(i);
    }
    out[rt]=dfsclock;
}

int segtree[maxn*50],root[maxn],cnt=0,lc[maxn*50],rc[maxn*50];//主席树
int p[maxn];//由于每个串都是多点更新  所以记录每个串更新结束时候的版本
inline void updata(int &rt,int last,int l,int r,int p)//dfs序的p位置+1
{
    rt=++cnt;
    segtree[rt]=segtree[last]+1;
    lc[rt]=lc[last],rc[rt]=rc[last];
    if(l == r) return ;
    int mid=(l+r)>>1;
    if(p <= mid) updata(lc[rt],lc[last],l,mid,p);
    else updata(rc[rt],rc[last],mid+1,r,p);
}
inline int query(int rt1,int rt2,int l,int r,int vl,int vr)//查询区间和
{
    if(vl<=l && r<=vr) return segtree[rt1]-segtree[rt2];
    int mid=(l+r)>>1;
    if(vr <= mid) return query(lc[rt1],lc[rt2],l,mid,vl,vr);
    if(vl > mid)  return query(rc[rt1],rc[rt2],mid+1,r,vl,vr);
    return query(lc[rt1],lc[rt2],l,mid,vl,vr)+query(rc[rt1],rc[rt2],mid+1,r,vl,vr);
}
int main()
{
    int n,q;
    scanf("%d %d",&n,&q);
    char s[maxn];
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        insert(s,i);
    }
    getfail();
    dfs(0);
    int cur=0;//主席树的版本号
    for(int i=1;i<=n;i++)
    {
        int rt=0;
        for(int j=0;j<len[i];j++) 
        {
            rt=tree[rt][sid[i][j]];cur++;
            updata(root[cur],root[cur-1],1,dfsclock,in[rt]);
        }
        p[i]=root[cur];//保存更新完i串的版本
    }
    while(q--)
    {
        int l,r,x;
        scanf("%d %d %d",&l,&r,&x);
        printf("%d\n",query(p[r],p[l-1],1,dfsclock,in[endstr[x]],out[endstr[x]]));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值