P9196 [JOI Open 2016] 销售基因链

题意

给定 $n$ 个由 AGUC 组成的字符串 $s_1,s_2,\dots,s_n$。$q$ 次查询以 $pre_i$ 为前缀,以 $suc_i$ 为后缀的字符串个数。

Solution

显然地,用 trie 树来处理前后缀,发现关键问题在于如何判断同时满足前后缀要求。发现将 $s_1,s_2,\dots,s_n$ 排序后,满足前缀为 $pre_i$ 的字符串编号连续,设其为 $[a_l,a_r]$。将 $s_1,s_2,\dots,s_n$ 翻转后排序,满足后缀为 $suc_i$ 的字符串编号同样连续,设其为 $[b_l,b_r]$。设字符串 $s_i$ 原来排序后的编号为 $u_i$,翻转排序后的编号为 $v_i$。则问题转化为同时满足 $u_i \in [a_l,a_r]$,$v_i\in [b_l,b_r]$ 的字符串个数。

考虑离线后二维数点解决,$[a_l,a_r]$ 和 $[b_l,b_r]$ 可用 trie 树快速维护。设 $\sum{\mid s_i\mid}=m$,字符串平均长度为 $len$,显然地,对于两个字符串,比较的最坏复杂度为 $\mathcal{O}(len)$,则排序复杂度为 $\mathcal{O}((\frac{m}{len}\log\frac{m}{len})\times len)$ 即 $\mathcal{O}(m \log \frac{m}{len})$,最坏情况下 $len=1$,即最坏复杂度为 $\mathcal{O}(m \log m)$。故总时间复杂度为 $\mathcal{O}(m\log m+\sum{\mid pre_i\mid}+\sum{\mid suc_i\mid})$,可以在时限内通过。

其实跑起来还是很快的,直接冲到最优解 rk2(rk1 坐在我边上)。

code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
struct node 
{
    int data;
    int son[5];
};
int tot;
struct node nd[2000010];
int l[2000010],r[2000010];
int cnt[2000010],ans[2000010];
string s[100010];
vector<tuple<string,int,int> > info[2000010];
int helper(char ch)
{
    switch(ch)
    {
        case 'A': return 1;
        case 'U': return 2;
        case 'G': return 3;
        case 'C': return 4;
    }
    return -1;
}
void insert(string s,int id)
{
    int fr=0;
    for(int i=0;i<s.length();i++)
    {
        int tmp=helper(s[i]);
        int to=nd[fr].son[tmp];
        if(to==0)
        {
            nd[fr].son[tmp]=++tot;
            to=tot;
            l[to]=id,r[to]=id;
        }
        l[to]=min(l[to],id),r[to]=max(r[to],id);
        fr=to;
    }
    return ;
}
void modify(string s)
{
    int fr=0;
    for(int i=s.length()-1;i>=0;i--)
    {
        int tmp=helper(s[i]);
        int to=nd[fr].son[tmp];
        if(to==0)
            nd[fr].son[tmp]=++tot,to=tot;
        cnt[to]++;
        fr=to;
    }
    return ;
}
int query(string s)
{
    int fr=0;
    for(int i=0;i<s.length();i++)
    {
        int tmp=helper(s[i]);
        int to=nd[fr].son[tmp];
        if(to==0)
            return -1;
        fr=to;
    }
    return fr;
}
int main(int argc,const char *argv[])
{
    int n=read(),q=read();
    for(int i=1;i<=n;i++)
        cin>>s[i];
    sort(s+1,s+n+1);
    for(int i=1;i<=n;i++)
        insert(s[i],i);
    for(int i=1;i<=q;i++)
    {
        string pre,suc;
        cin>>pre>>suc;
        int pos=query(pre);
        if(pos==-1)
            continue;
        reverse(suc.begin(),suc.end());
        info[r[pos]].push_back(make_tuple(suc,i,1));
        info[l[pos]-1].push_back(make_tuple(suc,i,-1));
    }
    memset(nd,0,sizeof nd);
    tot=0;
    for(int i=1;i<=n;i++)
    {
        modify(s[i]);
        for(auto j:info[i])
        {
            int pos=query(get<0>(j));
            ans[get<1>(j)]+=cnt[pos]*get<2>(j);
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}
posted @   Che_001  阅读(17)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示