LOJ#6031. 「雅礼集训 2017 Day1」字符串

Description

令$s$与$w$为两字符串,下标从$0$开始,定义:

1.$w[l,r]$表示字符串$w$在区间$[l,r]$中的子串;
2.$w$在$s$中出现的频率定义为$w$在$s$中出现的次数;
3.$f(s,w,l,r)$表示$w[l,r]$在$s$中出现的频率。
比如$f(ababa,aba,0,2)=2$。

现在给定串$s$,$m$个区间$[l,r]$和长度$k$,你要回答$q$个询问,每个询问给你一个长度为$k$的字符串$w$和两个整数$a,b$,求:

$$\sum_{i=a}^b f(s,w,l_i,r_i)$$

Solution

如果查询一个串在另一个串内的出现次数,可以建立模板串的SAM,所有后缀节点赋值为1,求文本串的子树和,可以DP预处理

$qk$为定值,分类讨论:

$k$较小时:

做法一:

离线询问,记录所有区间的出现次数,$O(k^2)$暴力做,时间复杂度$O(qk^{2.5})$

做法二:

记录所有区间的出现位置,对于每个询问二分求出有多少个区间$[L,R]$在$[a,b]$中,时间复杂度$O(qk^2\log m )$

$q$较小时

对于每个询问串,求出每个后缀最多匹配的长度和在SAM上匹配到的位置,计算答案时在parent树上倍增跳到合适的位置

时间复杂度$O(qm\log n)$

平衡两个时间复杂度后,发现两种方案时间复杂度非常相近,于是都可做

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,q,K,las=1,tot=1,buc[100005],sa[200005],siz[200005];
char s[100005];
struct Node{
    int l,r;
}node[100005];
struct SAM{
    int len,fa,ch[27];
}sam[200005];
inline int read(){
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
namespace task1{
    int B=20,L=1,R=0;
    long long ans[100005],vis[325][325];
    struct Que{
        int a,b,id;
        char str[325];
        bool operator <(const Que &z)const{return a/B==z.a/B?b<z.b:a<z.a;}
    }que[100005];
    void add(int x){++vis[node[x].l][node[x].r];}
    void del(int x){--vis[node[x].l][node[x].r];}
    long long calc(int x){
        long long ret=0;
        for(int i=1;i<=K;i++){
            int p=1;
            for(int j=i;j<=K;j++){
                int c=que[x].str[j]-'a';
                if(!sam[p].ch[c])break;
                p=sam[p].ch[c],ret+=1ll*vis[i][j]*siz[p];
            }
        }
        return ret;
    }
    void main(){
        for(int i=1;i<=q;i++)scanf("%s",que[i].str+1),que[i].a=read()+1,que[i].b=read()+1,que[i].id=i;
        sort(que+1,que+q+1);
        for(int i=1;i<=q;i++){
            while(L<que[i].a)del(L++);
            while(L>que[i].a)add(--L);
            while(R<que[i].b)add(++R);
            while(R>que[i].b)del(R--);
            ans[que[i].id]=calc(i);
        }
        for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
    }
}
namespace task2{
    int f[200005][21],pos[100005],len[100005];
    long long ans;
    char str[100005];
    long long find(int x,int v){
        for(int i=20;~i;i--)if(f[x][i]&&sam[f[x][i]].len>=v)x=f[x][i];
        return siz[x];
    }
    void main(){
        for(int i=1;i<=tot;i++)f[i][0]=sam[i].fa;
        for(int i=1;i<=20;i++)for(int j=1;j<=tot;j++)f[j][i]=f[f[j][i-1]][i-1];
        for(int i=1;i<=q;i++){
            scanf("%s",str+1),ans=0;
            int p=1,l=0;
            for(int j=1;j<=K;j++){
                int c=str[j]-'a';
                if(sam[p].ch[c])++l,p=sam[p].ch[c];
                else{
                    while(p>1&&!sam[p].ch[c])p=sam[p].fa;
                    if(sam[p].ch[c])l=sam[p].len+1,p=sam[p].ch[c];
                    else l=0;
                }
                pos[j]=p,len[j]=l;
            }
            int a=read()+1,b=read()+1;
            for(int j=a;j<=b;j++)if(len[node[j].r]>=node[j].r-node[j].l+1)ans+=find(pos[node[j].r],node[j].r-node[j].l+1);
            printf("%lld\n",ans);
        }
    }
}
void insert(int c){
    int p=las,np=las=++tot;
    sam[np].len=sam[p].len+1;
    for(;p&&!sam[p].ch[c];p=sam[p].fa)sam[p].ch[c]=np;
    if(!p)sam[np].fa=1;
    else{
        int q=sam[p].ch[c];
        if(sam[q].len==sam[p].len+1)sam[np].fa=q;
        else{
            int nq=++tot;
            sam[nq]=sam[q],sam[nq].len=sam[p].len+1,sam[q].fa=sam[np].fa=nq;
            for(;p&&sam[p].ch[c]==q;p=sam[p].fa)sam[p].ch[c]=nq;
        }
    }
    siz[np]=1;
}
int main(){
    n=read(),m=read(),q=read(),K=read(),scanf("%s",s+1);
    for(int i=1;i<=n;i++)insert(s[i]-'a');
    for(int i=1;i<=tot;i++)++buc[sam[i].len];
    for(int i=1;i<=n;i++)buc[i]+=buc[i-1];
    for(int i=tot;i;i--)sa[buc[sam[i].len]--]=i;
    for(int i=tot;i;i--)siz[sam[sa[i]].fa]+=siz[sa[i]];
    for(int i=1;i<=m;i++)node[i]=(Node){read()+1,read()+1};
    if(K<320)task1::main();
    else task2::main();
    return 0;
}
「雅礼集训 2017 Day1」字符串

 

posted @ 2021-03-23 22:14  QDK_Storm  阅读(54)  评论(0编辑  收藏  举报