P9196 [JOI Open 2016] 销售基因链
题意
给定 $n$ 个由 A
,G
,U
,C
组成的字符串 $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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)