后缀自动机+主席树
为这种精妙的字符串题调上一天真的是件很幸福的事呢!
题意转化过来之后就变成:给一个串S,有若干个询问,每次询问给字符串T,整数 L,整数 R,询问有多少个子串k使得k是T的子串但不是S[L]....S[R]的子串
1.首先我们考虑这个问题的弱化版本,每次询问的L≡1,R≡len(S) ——68pts
对于T的每个[ 1 , i ]的前缀子串,我们假设其后缀在S中能匹配到的最长子串的长度为maxlen,那么我们知道在T的后缀自动机的 i 对应的这个节点上所能表示的串的长度在len[fa[x]]~len[x]之间,那么对应 i 这个节点所不能表示出来的子串的个数就是len[x]-max(len[fa[x]],maxlen),因为这个子串可能完全匹配上了,所以我们还要对0取个max,因为完全匹配上之后应该在下一个节点处考虑这个前缀而非当前,因为当前串不可能在产生失配的贡献。然后我们将T的后缀自动机建出来,在parent树上跑一跑,累加一下答案就ok了。
那么maxlen怎么求呢?
我们考虑[1 , i ]的maxlen已经算出为now,怎么求[1 , i+1 ],此时假设在S的后缀自动机上匹配到p号点,那么我们看看p号点有没有连出去的s[i+1]字符的边 并且这个边所连向的点的endpos里面是否有>now的这个位置,如果有那[1 , i+1 ]的答案就是now+1了。如果没有连着的s[i+1]的这条边,那不用说,肯定是匹配不上的,但是如果有,那也不一定能匹配上!(注意这里很重要) 为什么呢?因为我们知道后缀自动机的节点其实代表的是每种类型的endpos,就算连出去有s[i+1]的边,但是它通向字符的位置(endpos)可能是在[1,now-1]之前的,这样显然是不合法的,因为你T串匹配上了now个,而最后一个字符匹配上S的位置却小于now,显然是不可行的。
那么不可行之后,如果now为0了 那我们应该直接break 因为无论如何之后都会失配了,如果now还是大于0,那么就让now--,因为[1 , i ] 后缀最长匹配到now,而[1 , i ]+s[ i ] 必然是只能从now-1开始匹配的,不然的话maxlen为now的时候一定能匹配上!
至此 我们已经掌握了68pts的写法
2.现在我们来考虑一下100分的写法
注意到前面我们考虑T从[1 , i]转移到[1 , i+1]时的那两个判断的条件1.“有连向s[i+1]的边”且2.“连向的那个点的endpos里面有在范围里的 ”,1条件不用说,很简单,让我们来想想2条件。在简化版的问题中,合法范围显然就是1~n。那么对于S中L~R这段子串来说呢?合法范围显然就是[ L , R ]。那么我们只需要额外考虑怎么判断该节点的endpos里是否有在合法范围里的,其它的做法与上面完全一致!
那么我们怎么来求出后缀自动机的某个节点上的endpos的值具体有哪些呢?
我们可以对每个点都建一个线段树来维护这个节点上的endpos中有无 i,有的话在线段树上标 1,反之标 0。然后考虑在parent树上,父亲的endpos是所有儿子的endpos的并,所以我们可以考虑在dfs序上建主席树,那么查询每个节点的endpos中是否有在范围里面,则可以查询主席树[dfn[x]-1,dfn[x]+si[x]-1]这段区间里差分线段树(这就是所有儿孙的线段树的并)中在你查询的合法区间内是否有值,若有,则可行,另之相反。(这里好像也可以直接将儿子的线段树合并到父亲的线段树上去,我一开始这么写的,拿了92分,mle了2个节点,可能是我合并姿势错了qwq)
好啦,现在你已经成功掌握了100分的做法了!
时间效率:O(nlogn)
空间效率:nlogn
代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define lch a[n].lc
#define rch a[n].rc
using namespace std;
const int maxn=1e6+10;
int T,n,len;
char s[maxn];
struct suda
{
struct daa{int ch[27],fa,len;}po[maxn],po2[maxn];
struct da{int lc,rc,si;}a[30*maxn];
int las,tot,las1,tot1,st[maxn],to[maxn],nt[maxn],topt,cnt,root[maxn],lc[maxn];
int dfn[maxn],line[maxn],si[maxn],dfn_num;
int ma[maxn],maxlen[maxn];
long long ans;
void init1()
{
memset(st,0,sizeof st);
memset(root,0,sizeof root);
memset(a,0,sizeof a);
memset(po,0,sizeof po);
memset(ma,0,sizeof ma);
memset(maxlen,0,sizeof maxlen);
memset(lc,0,sizeof lc);
cnt=0; topt=0; las=tot=1;
}
void init2()
{
for (int i=0;i<=2*len+1;i++)
{
st[i]=0;
for (int j=0;j<26;j++) po2[i].ch[j]=0;
po2[i].fa=0; po2[i].len=0;
}
topt=0; las1=tot1=1;
}
void insert1(int x,int id)
{
int p=las,np=las=++tot; po[np].len=po[p].len+1;
lc[np]=id;
for (;p&&(!po[p].ch[x]);p=po[p].fa) po[p].ch[x]=np;
if (!p) po[np].fa=1;
else
{
int q=po[p].ch[x];
if (po[q].len==po[p].len+1) po[np].fa=q;
else
{
int nq=++tot; po[nq]=po[q];
po[nq].len=po[p].len+1;
po[q].fa=po[np].fa=nq;
for (;p&&(po[p].ch[x]==q);p=po[p].fa) po[p].ch[x]=nq;
}
}
}
void add(int x,int y)
{
to[++topt]=y; nt[topt]=st[x]; st[x]=topt;
}
void updata(int n){a[n].si=a[lch].si+a[rch].si;}
void build_tree(int &n,int l,int r)
{
n=++cnt; if (l==r) {a[n].si=0; return;}
int mid=(l+r)>>1;
build_tree(lch,l,mid); build_tree(rch,mid+1,r);
updata(n);
}
void tree_add(int &n,int old,int l,int r,int lc)
{
n=++cnt;
if (l==r) {a[n].si=a[old].si+1; return;}
int mid=(l+r)>>1;
if (lc<=mid) {rch=a[old].rc; tree_add(lch,a[old].lc,l,mid,lc);}
else {lch=a[old].lc; tree_add(rch,a[old].rc,mid+1,r,lc);}
updata(n);
}
int qury(int n,int L,int R,int l,int r)
{
if (l>r) return 0;
if (L==l && R==r) return a[n].si;
int mid=(L+R)>>1;
if (r<=mid) return qury(lch,L,mid,l,r);
else if (l>=mid+1) return qury(rch,mid+1,R,l,r);
else return qury(lch,L,mid,l,mid)+qury(rch,mid+1,R,mid+1,r);
}
void dfs(int x)
{
dfn[x]=++dfn_num; line[dfn_num]=x;
if (lc[x])
{
tree_add(root[dfn[x]],root[dfn[x]-1],1,n,lc[x]);
}else root[dfn[x]]=root[dfn[x]-1];
int p=st[x]; si[x]=1;
while (p)
{
dfs(to[p]); si[x]+=si[to[p]];
p=nt[p];
}
//printf("kkqqqq id=%d si=%d treesize=%d\n",x,sii[x],a[root[x]].si);
}
void prework()
{
//printf("lalalalala %d\n",po[1].ch[18]);
for (int i=2;i<=tot;i++)
{
add(po[i].fa,i);
//printf("edge: %d->%d\n",po[i].fa,i);
}
build_tree(root[0],1,n);
dfs(1);
}
void getma(int l,int r)
{
for (int i=0;i<=2*len+10;i++) ma[i]=maxlen[i]=0;
int p=1,now=0;
for (int i=1;i<=len;i++)
{
int x=s[i]-'a';
//printf("kkkkq %d %d %d\n",x,po[p].ch[x],qury(root[po[p].ch[x]],1,n,l+now,r));
//printf("%d:\n",i);
while (1)
{
//printf("%d ",p);
if (po[p].ch[x] && (qury(root[dfn[po[p].ch[x]]+si[po[p].ch[x]]-1],1,n,l+now,r)-qury(root[dfn[po[p].ch[x]]-1],1,n,l+now,r)>0))
{
p=po[p].ch[x]; now++; break;
}
if (!now) break;
now--;
if (now==po[po[p].fa].len) p=po[p].fa;
}
//printf("\n");
maxlen[i]=now;
//printf("%d %d\n",i,maxlen[i]);
}
}
void insert2(int x,int id)
{
int p=las1,np=las1=++tot1; po2[np].len=po2[p].len+1;
ma[np]=maxlen[id];
//printf("kqkqkqqlala %d %d %d\n",np,maxlen[id],id);
for (;p&&(!po2[p].ch[x]);p=po2[p].fa) po2[p].ch[x]=np;
if (!p) po2[np].fa=1;
else
{
int q=po2[p].ch[x];
if (po2[q].len==po2[p].len+1) po2[np].fa=q;
else
{
int nq=++tot1; po2[nq]=po2[q];
po2[nq].len=po2[p].len+1;
po2[q].fa=po2[np].fa=nq;
for (;p&&(po2[p].ch[x]==q);p=po2[p].fa) po2[p].ch[x]=nq;
}
}
}
void calc(int x)
{
int p=st[x];
while (p)
{
calc(to[p]);
ma[x]=max(ma[x],ma[to[p]]);
p=nt[p];
}
//printf("kkqq %d %d\n",x,ma[x]);
ans+=max(0,po2[x].len-max(ma[x],po2[po2[x].fa].len));
}
long long solve()
{
ans=0;
for (int i=2;i<=tot1;i++) add(po2[i].fa,i);
calc(1);
return ans;
}
}SAM;
int main()
{
scanf("%s",s+1); n=strlen(s+1);
SAM.init1();
for (int i=1;i<=n;i++) SAM.insert1(s[i]-'a',i);
SAM.prework();
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1); len=strlen(s+1);
int l,r; scanf("%d%d",&l,&r);
SAM.getma(l,r); SAM.init2();
for (int i=1;i<=len;i++) SAM.insert2(s[i]-'a',i);
printf("%lld\n",SAM.solve());
}
return 0;
}
/*
scbamgepe
1
smape 2 7
*/