loj6031.「雅礼集训 2017 Day1」字符串

题意

注意到询问串的长度\(k\)是给定的,同时\(\sum\limits len_w=kq\leqslant10^5\),我们发现\(k,q\)之间一个大了另一个必定会小,因此我们对\(k\)分类讨论:
首先肯定要对\(s\)建一个后缀自动机,对每个点维护\(endpos\)集合的大小。

\(k\geqslant\sqrt{n}\)

这时\(q\leqslant\sqrt{n}\),我们直接在\(SAM\)暴力匹配\(w\),找出所有\(w\)的前缀对应的节点,之后枚举询问,倍增跳到\(w[l_i...r_i]\)这个节点,将答案加上该节点的\(endpos\)集合大小即可。

\(k<\sqrt{n}\)

这时\(m\)组询问中的种类很少,我们可以枚举\(w\)的所有子串,算出它在\([l,r]\)这段询问区间中出现的次数,乘上它的答案加到答案中。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define pii pair<int,int>
#define mkp make_pair
#define fir first
#define sec second
const int maxn=2e5+10;
const int maxt=410;
int n,m,Q,K,tot,cnt_edge,t;
int head[maxn],size[maxn],pos[maxn],posl[maxn];
int f[maxn][18];
char s[maxn];
inline int read()
{
    char c=getchar();re int res=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
    return res*f;
}
struct edge{int to,nxt;}e[maxn];
inline void add_edge(int u,int v)
{
    e[++cnt_edge].nxt=head[u];
    head[u]=cnt_edge;
    e[cnt_edge].to=v;
}
struct Query{int l,r;}qr[maxn];
struct SAM
{
    int last,tot;
    int fa[maxn],len[maxn];
    int ch[maxn][26];
    SAM(){last=tot=1;}
    inline void add(int c)
    {
        re int now=++tot;len[now]=len[last]+1;
        re int p=last;last=now;
        while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
        if(!p){fa[now]=1;return;}
        re int q=ch[p][c];
        if(len[q]==len[p]+1)fa[now]=q;
        else
        {
            re int nowq=++tot;len[nowq]=len[p]+1;
            memcpy(ch[nowq],ch[q],sizeof(ch[q]));
            fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
            while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
        }
    }
}sam;
void dfs(int x)
{
    for(re int i=1;i<=17;i++)f[x][i]=f[f[x][i-1]][i-1];
    for(re int i=head[x];i;i=e[i].nxt)
    {
        re int y=e[i].to;
        f[y][0]=x;dfs(y);
        size[x]+=size[y];
    }
}
inline void solve1()
{
    vector<int>a[maxt][maxt];
    for(int i=1;i<=m;i++)a[qr[i].l][qr[i].r].push_back(i);
    while(Q--)
    {
        scanf("%s",s+1);
        int l=read()+1,r=read()+1;
        ll res=0;
        for(int i=1;i<=K;i++)
            for(int j=i,now=1;j<=K;j++)
            {
                now=sam.ch[now][s[j]-'a'];
                if(!now)break;
                int ql=lower_bound(a[i][j].begin(),a[i][j].end(),l)-a[i][j].begin();
                int qr=upper_bound(a[i][j].begin(),a[i][j].end(),r)-a[i][j].begin();
                res+=1ll*(qr-ql)*size[now];
            }
        printf("%lld\n",res);
    }
}
inline int find(int l,int r)
{
    re int now=pos[r];
    for(re int i=17;~i;i--)if(sam.len[f[now][i]]>=min(r-l+1,posl[r]))now=f[now][i];
    return now;
}
inline int solve(int l,int r)
{
    if(!pos[r]||posl[r]<(r-l+1))return 0;;
    re int now=find(l,r);
    if(sam.len[now]>=r-l+1)return size[now];
    else return 0;
}
inline void solve2()
{
    while(Q--)
    {
        scanf("%s",s+1);
        re int l=read()+1,r=read()+1;
        re int now=1,nowl=0,res=0;
        for(re int i=1;i<=K;i++)
        {
            re int c=s[i]-'a';
            while(!sam.ch[now][c]&&now)now=sam.fa[now],nowl=sam.len[now];
            if(!now){now=1;pos[i]=1;nowl=0;posl[i]=0;continue;}
            now=sam.ch[now][c];pos[i]=now;
            nowl++;posl[i]=nowl;
        }   
        for(re int i=l;i<=r;i++)res+=solve(qr[i].l,qr[i].r);
        printf("%d\n",res);
    }
}
int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
    n=read(),m=read(),Q=read(),K=read();t=330;
    scanf("%s",s+1);
    for(re int i=1;i<=n;i++)sam.add(s[i]-'a'),size[sam.last]=1;
    for(re int i=2;i<=sam.tot;i++)add_edge(sam.fa[i],i);
    dfs(1);
    for(re int i=1;i<=m;i++)qr[i].l=read()+1,qr[i].r=read()+1;
    if(K<=t)solve1();
    else solve2();
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/nofind/p/12149765.html
今日推荐