题目大意
给定n个字符串和q个询问
每次询问在这n个字符串中,有多少个字符串同时满足
1. 字符串a是它的前缀
2. 字符串b是它的后缀
解题思路
先对字符串排序,倒着建可持久化trie,二分查找前缀,后缀在线查询。
code
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=5*1e4;
int n,q,cnt,t[maxn*100+10],son[maxn*100+1][10];
struct rec{
char s[101];
int len;
};
rec s[maxn+1],a,b;
bool cmp(rec x,rec y){
int tmp=max(x.len,y.len);
fo(i,1,tmp)if(x.s[i]<y.s[i])return 1;
else if(x.s[i]>y.s[i])return 0;
return 0;
}
int cmp2(rec x,rec y,int tmp){
fo(i,1,tmp)if(x.s[i]>y.s[i])return 1;
else if(x.s[i]<y.s[i])return 0;
return 2;
}
void oper(int now,int pre,rec &s,int p,int lim){
t[now]=t[pre]+1;
fo(i,0,9)if(i!=s.s[p]-'a')son[now][i]=son[pre][i];
if((!p)||(p==lim))return;
if(!son[now][s.s[p]-'a'])son[now][s.s[p]-'a']=++cnt;
oper(son[now][s.s[p]-'a'],son[pre][s.s[p]-'a'],s,p-1,lim);
}
int qury(int now,rec &s,int p,int lim){
if((!p)||(p==lim))return t[now];
return qury(son[now][s.s[p]-'a'],s,p-1,lim);
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d\n",&n,&q);
fo(i,1,n)scanf("%s\n",&s[i].s[1]),s[i].len=strlen(&s[i].s[1]);
sort(s+1,s+n+1,cmp);
cnt=n;
fo(i,1,n){
if(i==30000){
int bb;
bb++;
}
oper(i,i-1,s[i],s[i].len,s[i].len-100);
}
fo(i,1,q){
scanf("%s\n",&a.s[1]),a.len=strlen(&a.s[1]);
scanf("%s\n",&b.s[1]),b.len=strlen(&b.s[1]);
int l=1,r=n,mid;
while(l!=r){
mid=(l+r)/2;
if(cmp2(s[mid],a,a.len))r=mid;
else l=mid+1;
}
if(cmp2(s[l],a,a.len)!=2){printf("0\n");continue;}
int ans=qury(l-1,b,b.len,b.len-100);
l=1,r=n;
while(l!=r){
mid=(l+r)/2;
if(cmp2(s[mid],a,a.len)==1)r=mid;
else l=mid+1;
}
if((l==n)&&(cmp2(s[l],a,a.len)!=1))l++;
ans=qury(l-1,b,b.len,b.len-100)-ans;
printf("%d\n",ans);
}
return 0;
}