你的名字
题目背景
实力强大的小A 被选为了ION2018 的出题人,现在他需要解决题目的命名问题。
题目描述
小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。
由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字必须是那一年的命名串的一个非空连续子串,且不能和前一年的任何一道题目的名字相同。
由于一些特殊的原因,小A 不知道ION2017 每道题的名字,但是他通过一些特殊手段得到了ION2017 的命名串,现在小A 有Q 次询问:每次给定ION2017 的命名串和ION2018 的命名串,求有几种题目的命名,使得这个名字一定满足命题委员会的规定,即是ION2018 的命名串的一个非空连续子串且一定不会和ION2017 的任何一道题目的名字相同。
由于一些特殊原因,所有询问给出的ION2017 的命名串都是某个串的连续子串,详细可见输入格式。
输入输出格式
输入格式:第一行一个字符串S ,之后询问给出的ION2017 的命名串都是S 的连续子串。 第二行一个正整数Q,表示询问次数。 接下来Q 行,每行有一个字符串T 和两个正整数$l,r$,表示询问如果ION2017 的 命名串是$S [l..r]$,ION2018 的命名串是T 的话,有几种命名方式一定满足规定。
输出格式:输出Q 行,第i 行一个非负整数表示第i 个询问的答案。
输入输出样例
说明
测试点 | $|S|leq$ | $Qleq $ | $sum |T|leq $ | 其他限制 |
---|---|---|---|---|
1 | $200$ | $200$ | $40000$ | $|T|leq 200$ |
2 | $1000$ | $200$ | $40000$ | $|T|leq 200$ |
3 | $1000$ | $200$ | $40000$ | $|T|leq 200$ |
4 | $1000$ | $200$ | $5 imes 10^5$ | 无 |
5 | $1000$ | $200$ | $5 imes 10^5$ | 无 |
6 | $5 imes 10^5$ | $1$ | $5 imes 10^5$ | 无 |
7 | $5 imes 10^5$ | $1$ | $5 imes 10^5$ | 无 |
8 | $10^5$ | $10^5$ | $2 imes 10^5$ | 无 |
9 | $10^5$ | $10^5$ | $2 imes 10^5$ | 字符串随机 |
10 | $2 imes 10^5$ | $10^5$ | $4 imes 10^5$ | 无 |
11 | $2 imes 10^5$ | $10^5$ | $4 imes 10^5$ | 字符串随机 |
12 | $3 imes 10^5$ | $10^5$ | $6 imes 10^5$ | 无 |
13 | $3 imes 10^5$ | $10^5$ | $6 imes 10^5$ | 字符串随机 |
14 | $4 imes 10^5$ | $10^5$ | $8 imes 10^5$ | 无 |
15 | $4 imes 10^5$ | $10^5$ | $8 imes 10^5$ | 字符串随机 |
16 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
17 | $5 imes 10^5$ | $10^5$ | $10^6$ | 字符串随机 |
18 | $2 imes 10^5$ | $10^5$ | $10^6$ | 无 |
19 | $3 imes 10^5$ | $10^5$ | $10^6$ | 无 |
20 | $4 imes 10^5$ | $10^5$ | $10^6$ | 无 |
21 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
22 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
23 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
24 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
25 | $5 imes 10^5$ | $10^5$ | $10^6$ | 无 |
对于前17个测试点的所有询问有$l=1,r=|S|$
对于所有数据,保证 $1leq l leq r leq |S|$,$1leq |T|leq 5 imes 10^5$
题解
补集转换一步,问题变成 ((T) 的本质不同的子串数) - ((T) 与 (S[l_i:r_i]) 的本质不同的公共子串数)。
第一个问题是 SAM 模板。
针对第二个问题,我们要找到 (T) 的 SAM 上的每个节点 (x) 在 (T) 中合法长度中 ([len_{fa_x}+1,len_x]) 和 在 (S) 中的合法长度的交集。那么我们便要去找每个节点 (x) 能匹配到 (S[l_i:r_i]) 中某个串时的最大后缀长度 (res_x)。
求 (len_x) 可以把 (T) 丢到 (S) 的 SAM 上去匹配。对于每个 (T) 的前缀找到在 (S) 中的最长匹配位置,然后不断删去首字母,直到能在 (S[l_i:r_i]) 放下。这个用线段树合并维护 right 集合即可。
时间复杂度(O((|S|+sum|T|)log |S|))
co int N=2e6;
char s[N];
int res[N],n,q;
// Interval Tree
namespace T{
int siz[N*25],lc[N*25],rc[N*25],tot;
void insert(int&x,int l,int r,int p){
if(!x) x=++tot;
++siz[x];
if(l==r) return;
int mid=l+r>>1;
if(p<=mid) insert(lc[x],l,mid,p);
else insert(rc[x],mid+1,r,p);
}
int merge(int x,int y){
if(!x||!y) return x+y;
int o=++tot;
siz[o]=siz[x]+siz[y];
lc[o]=merge(lc[x],lc[y]);
rc[o]=merge(rc[x],rc[y]);
return o;
}
int query(int x,int l,int r,int ql,int qr){
if(!x) return 0;
if(ql<=l&&r<=qr) return siz[x];
int mid=l+r>>1;
if(qr<=mid) return query(lc[x],l,mid,ql,qr);
if(ql>mid) return query(rc[x],mid+1,r,ql,qr);
return query(lc[x],l,mid,ql,qr)+query(rc[x],mid+1,r,ql,qr);
}
}
// Suffix Automaton
vector<int> vec[N];
namespace S1{ // S
vector<int> e[N];
int last=1,tot=1;
int ch[N][26],fa[N],len[N],rt[N];
void extend(int c,int po){
int p=last,cur=last=++tot;
T::insert(rt[cur],1,n,po);
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
if(!p) fa[cur]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else{
int clone=++tot;
memcpy(ch[clone],ch[q],sizeof ch[q]);
fa[clone]=fa[q],len[clone]=len[p]+1;
fa[cur]=fa[q]=clone;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
}
}
}
void dfs(int u){
for(int i=0;i<e[u].size();++i)
dfs(e[u][i]),rt[u]=T::merge(rt[u],rt[e[u][i]]);
}
void build_tree(){
for(int i=2;i<=tot;++i) e[fa[i]].push_back(i);
dfs(1);
}
void solve(char*s,int L,int R){
int length=strlen(s+1);
for(int i=1,p=1,now=0;i<=length;++i){
int c=s[i]-'a';
while(!ch[p][c]&&p) p=fa[p],now=len[p];
if(!p) {p=1,now=0;continue;}
p=ch[p][c],++now;
while(p>1){
if(T::query(rt[p],1,n,L+now-1,R)) break;
if(--now==len[fa[p]]) p=fa[p];
}
if(p==1) continue;
for(int j=0;j<vec[i].size();++j)
res[vec[i][j]]=max(res[vec[i][j]],now);
}
}
}
namespace S2{ // T
int last,tot;
int ch[N][26],fa[N],len[N];
void clear(){
for(int i=1;i<=tot;++i){
fa[i]=len[i]=res[i]=0;
memset(ch[i],0,sizeof ch[i]);
}
last=tot=1;
}
void extend(int c,int po){
int p=last,cur=last=++tot;
len[cur]=len[p]+1,vec[po].push_back(cur);
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
if(!p) fa[cur]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else{
int clone=++tot;
memcpy(ch[clone],ch[q],sizeof ch[q]);
fa[clone]=fa[q],len[clone]=len[p]+1,vec[po].push_back(clone);
fa[cur]=fa[q]=clone;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
}
}
}
void solve(){
ll ans1=0,ans2=0;
for(int i=1;i<=tot;++i){
if(res[i]>len[fa[i]]) ans2+=min(res[i],len[i])-len[fa[i]];
ans1+=len[i]-len[fa[i]];
}
printf("%lld
",ans1-ans2);
}
}
int main(){
// freopen("name.in","r",stdin),freopen("name.out","w",stdout);
scanf("%s",s+1),n=strlen(s+1);
for(int i=1;i<=n;++i) S1::extend(s[i]-'a',i);
S1::build_tree();
read(q);
for(int m,L,R;q--;){
scanf("%s",s+1),m=strlen(s+1);
read(L),read(R);
for(int i=1;i<=m;++i) vec[i].clear();
S2::clear();
for(int i=1;i<=m;++i) S2::extend(s[i]-'a',i);
S1::solve(s,L,R),S2::solve();
}
return 0;
}