题目大意
SA+莫队
我们把所有字符串连起来,中间有分割符分开,然后求SA。
那么,一个子串所在第几个单词可以看以其为前缀的后缀包含了几个分隔符。
对于每个询问,我们二分找出在SA上的对应区间。于是,询问可以改成这样:求[l,r]权值在[x,y]的权值种类数。
可以用莫队做,用分块维护就可以消掉log,这是经典套路。
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=500000+10,B=550,BB=250;
char h[maxn];
int s[maxn*2],num[maxn*2],sa[maxn*2],rank[maxn*4];
int b[maxn*2],c[maxn*2],d[maxn*2];
struct dong{
int l,r,x,y,id;
bool p;
} ask[maxn];
int belong[maxn*2],bel[maxn],cnt[maxn/BB+10],a[maxn],ans[maxn],sta[100];
int i,j,k,l,r,ll,rr,mid,t,n,m,mx,len,top;
int read(){
int x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void getsa(){
fo(i,1,n) rank[i]=s[i];
j=1;
while (j<=n){
fo(i,0,n) b[i]=0;
fo(i,1,n) b[rank[i+j]]++;
fo(i,1,n) b[i]+=b[i-1];
fd(i,n,1) c[b[rank[i+j]]--]=i;
fo(i,0,n) b[i]=0;
fo(i,1,n) b[rank[i]]++;
fo(i,1,n) b[i]+=b[i-1];
fd(i,n,1) d[b[rank[c[i]]]--]=c[i];
t=0;
fo(i,1,n){
if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]+j]!=rank[d[i-1]+j]) t++;
c[d[i]]=t;
}
fo(i,1,n) rank[i]=c[i];
if (t==n) break;
j*=2;
}
fo(i,1,n) sa[rank[i]]=i;
}
bool cmp(dong a,dong b){
return belong[a.l]<belong[b.l]||belong[a.l]==belong[b.l]&&a.r<b.r;
}
void in(int x){
a[x]++;
if (a[x]==1) cnt[bel[x]]++;
}
void out(int x){
a[x]--;
if (!a[x]) cnt[bel[x]]--;
}
int query(int j,int k){
int i,l=bel[j],r=bel[k],t=0;
if (r-l<=1){
fo(i,j,k)
if (a[i]>0) t++;
return t;
}
fo(i,l+1,r-1) t+=cnt[i];
fo(i,j,l*BB)
if (a[i]>0) t++;
fo(i,(r-1)*BB+1,k)
if (a[i]>0) t++;
return t;
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top) putchar(sta[top--]+'0');
putchar('\n');
}
int main(){
n=read();mx=m=read();
fo(i,1,n){
scanf("%s",h+1);
fo(j,len+1,len+strlen(h+1)) s[j]=h[j-len]-'a'+1;
len+=strlen(h+1);
s[++len]=3;
}
n=len;
fd(i,n,1) num[i]=num[i+1]+(s[i]==3);
getsa();
fo(i,1,m){
ask[i].x=num[1]-read()+1;ask[i].y=num[1]-read()+1;
swap(ask[i].x,ask[i].y);
ask[i].id=i;
scanf("%s",h+1);
l=1;r=n;
fo(j,1,strlen(h+1)){
ll=l;rr=r;
while (ll<rr){
mid=(ll+rr)/2;
if (s[sa[mid]+j-1]<h[j]-'a'+1) ll=mid+1;else rr=mid;
}
k=ll;
if (s[sa[k]+j-1]!=h[j]-'a'+1){
ask[i].p=1;
break;
}
ll=l;rr=r;
while (ll<rr){
mid=(ll+rr+1)/2;
if (s[sa[mid]+j-1]>h[j]-'a'+1) rr=mid-1;else ll=mid;
}
t=ll;
if (s[sa[t]+j-1]!=h[j]-'a'+1){
ask[i].p=1;
break;
}
l=k;r=t;
}
ask[i].l=l;ask[i].r=r;
}
t=0;
fo(i,1,m)
if (!ask[i].p) ask[++t]=ask[i];
m=t;
fo(i,1,n) belong[i]=(i-1)/B+1;
fo(i,1,num[1]) bel[i]=(i-1)/BB+1;
sort(ask+1,ask+m+1,cmp);
l=1;r=0;
fo(i,1,m){
while (l<ask[i].l) out(num[sa[l++]]);
while (l>ask[i].l) in(num[sa[--l]]);
while (r<ask[i].r) in(num[sa[++r]]);
while (r>ask[i].r) out(num[sa[r--]]);
ans[ask[i].id]=query(ask[i].x,ask[i].y);
}
fo(i,1,mx) write(ans[i]);
//fo(i,1,mx) printf("%d\n",ans[i]);
}