给定n个由小写字母组成的字符串。现在有m个询问,每个询问指定两个字符串s1,s2.要求回答:在给定的n个字符串中,有多少个字符串s满足:s1是s的前缀且s2是s的后缀。强制在线!
n个字符串的总长度L1≤2⋅106,m个s1,s2的总长度L2≤2⋅106,n≤2000,m≤100000.
时间限制:1s
空间限制:256M (512M) (原题限制256M,这个太丧心病狂了!作为蒟蒻,我只会写512M限制的!)
对给定的n个字符串建一棵字典树.对于询问,我们先在trie上匹配,假设最后停在了p节点,那么所有满足条件的字符串s的末尾节点一定是在p的子树中的。如果我们把原字符串按照字典序排序(其实就是trie的dfs序),那么满足条件的s所对应的编号一定落在某个连续区间内。于是我们用可持久化trie维护后缀就好了。
第一次写可持久化trie,感觉还算比较好写。
看别人的题解,都是自定义cmp函数,然后sort一下。只有我蠢蠢地按dfs序排序。
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 2000100
char s[MAXN],t[MAXN];
int _l[MAXN],_r[MAXN];
int n,m,tot=1;
int to[MAXN][26],rt[MAXN],nxt[MAXN][26],summ[MAXN],mn[MAXN],mx[MAXN],order[MAXN];
int C[MAXN];
vector<int>P[MAXN];
void ins(int id)
{
int x,p=1;
for(int i=0;t[i]!='\0';i++)
{
x=t[i]-'a';
if(!to[p][x])
{
to[p][x]=++tot;
}
p=to[p][x];
}
P[p].push_back(id);
return ;
}
int tim=0;
const int inf=0x3c3c3c3c;
void dfs(int p)
{
mn[p]=inf;
mx[p]=0;
for(int i=0;ifor(int i=0;i<26;i++)
{
if(to[p][i])
{
dfs(to[p][i]);
mn[p]=min(mn[p],mn[to[p][i]]);
mx[p]=max(mx[p],mx[to[p][i]]);
}
}
return ;
}
bool cmp(int a,int b)
{
return order[a]void ins2(int& p,int f,int d,int pp)
{
p=++tot;
summ[p]=summ[f]+1;
memcpy(nxt[p],nxt[f],sizeof(nxt[f]));
if(d>pp)
{
ins2(nxt[p][s[d]-'a'],nxt[f][s[d]-'a'],d-1,pp);
}
return ;
}
int lstans=0;
int main()
{
scanf("%d",&n);
for(int i=1,tmp;i<=n;i++)
{
scanf("%s",t);
_l[i]=_r[i-1]+1;
tmp=strlen(t);
_r[i]=_l[i]+tmp-1;
for(int j=0;j1);
sort(C+1,C+1+n,cmp);
tot=1;
for(int i=1,I;i<=n;i++)
{
I=C[i];
ins2(rt[i],rt[i-1],_r[I],_l[I]);
}
int L,R;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",t);
L=inf;
R=0;
int x=1;
for(int c,j=0;t[j]!='\0';j++)
{
c=t[j]-'a';
c=c+lstans;
c%=26;//这个傻逼错误,我调了半天才发现!!c-=c>=26?26:0;
x=to[x][c];
}
if(x)
{
L=mn[x];
R=mx[x];
}
scanf("%s",t);
if(L>R)
{
lstans=0;
}
else
{
int x=rt[L-1],y=rt[R],c,nn=strlen(t);
for(int j=nn-1;j>=0;j--)
{
c=t[j]-'a';
c=c+lstans;
c%=26;//c-=c>=26?26:0;
x=nxt[x][c];
y=nxt[y][c];
}
lstans=summ[y]-summ[x];
}
printf("%d\n",lstans);
}
return 0;
}