【GDOI2017模拟9.10】子串

12 篇文章 0 订阅
8 篇文章 0 订阅

Description

给出n个字符串Si,m次询问,第i次询问Sli~Sri这些字符串中有多少个是字符串pi的母串。
|Si|,|pi|<=5105

Solution

看到多串匹配就想到了AC自动机。
然而辛辛苦苦打完之后发现只有自己傻傻地写的那么辣鸡。
在线OrzSAM和SA分块做法。

显然我们需要离线回答。
把所有的pi建一棵AC自动机。
然后把询问差分,变成前缀的形式。
那么我们只需要每次把一个主串放在AC自动机上跑一跑,然后把经过的点和它在fail树中到根的路径全部+1.但是每个点只会加一次(因为没让你统计出现次数)
于是我们可以把这个操作用树状数组维护dfs序的形式来完成。
如果我们按dfs序的顺序进行修改操作,那么x和la所重复的路径一定是lca(x,la)到根的这段路。
那么我们可以求出所有点然后按dfs序排序,然后操作。
也就是说我们要兹瓷修改一个点到根的路径,以及单点查询。
那么我们可以这样做:如果一个操作y对点x有影响,那么y一定存在于x的子树当中。
于是我们这个东西等价于单点修改,子树询问。
然后这道题就解决了。
那些让C++选手去写vector的出题人都应该拖出去续了

Code

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=la[a];i;i=ne[i])
#define N 500005
using namespace std;
typedef vector<char> vec;
struct note{int x,bz,id,p;}a[N*2];
bool cmp(note x,note y) {return x.x<y.x;}
int t[N][26],next[N],d[N],h[N],dfn[N],cout[N],an[N],tr[N];
int to[N],ne[N],la[N],in[N],out[N],p[N],f[N][19],deep[N];
int n,m,x,y,le,ri,tot,top,bz,l;
vec s[N];
char st[N];
bool cmd(int x,int y) {return in[x]<in[y];}
void link(int x,int y) {
    to[++l]=y;ne[l]=la[x];la[x]=l;
}
void insert(int x,int y) {
    for(;x<=tot;x+=x&-x) tr[x]+=y;
}
int query(int x) {
    int ans=0;
    for(;x;x-=x&-x) ans+=tr[x];
    return ans;
}
void makefail() {
    int i=0,j=1;d[1]=1;
    while (i<j) {
        i++;
        fo(k,0,25) if (t[d[i]][k]) {
            int x=t[d[i]][k];
            if (d[i]!=1) {
                int y=next[d[i]];
                while (y&&!t[y][k]) y=next[y];
                if (y) next[x]=t[y][k];
                else next[x]=1;
            } else next[x]=1;
            link(next[x],x);
            d[++j]=x;
        }
    }
}
void add(vec s) {
    int x=1;
    fo(i,0,s.size()-1) {
        int y=s[i]-'a';
        while (x&&!t[x][y]) x=next[x];
        x=t[x][y];if (!x) x=1;p[++p[0]]=x;
    }
}
void dfs(int x) {
    in[x]=++tot;f[x][0]=next[x];deep[x]=deep[next[x]]+1;
    fo(j,1,18) f[x][j]=f[f[x][j-1]][j-1];
    rep(i,x) dfs(to[i]);
    out[x]=tot;
}
int lca(int x,int y) {
    if (deep[x]<deep[y]) swap(x,y);
    fd(j,18,0) if (deep[f[x][j]]>deep[y]) x=f[x][j];
    if (deep[x]!=deep[y]) x=f[x][0];
    fd(j,18,0) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    if (x!=y) return f[x][0]; else return x;
}
int main() {
    scanf("%d%d",&n,&m);top=1;
    fo(i,1,n) {
        scanf("%s",st);
        fo(j,0,strlen(st)-1) s[i].push_back(st[j]);
    }
    fo(i,1,m) {
        scanf("%d%d%s",&le,&ri,st);int x=1;
        fo(j,0,strlen(st)-1) {
            int y=st[j]-'a';
            if (!t[x][y]) t[x][y]=++top;x=t[x][y];
        }
        a[++tot].x=le-1;a[tot].bz=-1;a[tot].id=i;a[tot].p=x;
        a[++tot].x=ri;a[tot].bz=1;a[tot].id=i;a[tot].p=x;
    }
    sort(a+1,a+tot+1,cmp);makefail();tot=0;
    dfs(1);int j=0;while (!a[j+1].x) j++;
    fo(i,1,n) {
        p[0]=0;add(s[i]);sort(p+1,p+p[0]+1,cmd);
        fo(k,1,p[0]) {
            insert(in[p[k]],1);
            if (k!=1) insert(in[lca(p[k-1],p[k])],-1);
        }
        while (a[j+1].x==i) j++,an[a[j].id]+=a[j].bz*(query(out[a[j].p])-query(in[a[j].p]-1));
    }
    fo(i,1,m) printf("%d\n",an[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值