传送门
题意:
给你一个长度位的串,一共有组询问,每次询问给你一个数,你要将原来的串在位置处分开,构造出两个不同的字符串,。现在要问你和的公共子串的个数。
分析:
讲真这又是一个简化版的原题呀 。(原题)
我们发现,在这个题中,询问非常多,字符串的长度并不是非常的大。因此我们可以考虑先把答案预处理出来。
因为涉及两个串的子串个数问题,因此我们不妨考虑使用后缀自动机预处理。
我们对其中一个串建立后缀自动机,我们用拓扑排序自底向上的对整个自动机的集合的大小进行更新(因为对于某个结点,他沿着后缀连接走到的结点必然是结点的后缀,故结点的答案必定要更新到结点)。
更新完集合的大小后,我们需要维护一个前缀和,代表到达当前结点时,终止点在的所有子串中,在串中出现的次数。
完成上述工作之后,我们只需要把串丢到后缀自动机上去匹配,每次去统计答案并记录下来供次询问查询即可。
整体时间复杂度:
代码:
#include <bits/stdc++.h>
#define maxn 4005
using namespace std;
char str[maxn];
typedef unsigned long long ll;
struct SAM{
int next[maxn*2][26],fa[maxn*2],len[maxn*2],last,cnt=0;
ll cntA[maxn*2],A[maxn*2];
ll num[maxn*2],sum[maxn*2];
SAM(){clear();}
void clear(){
last=cnt=1;
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
memset(next[1],0,sizeof(next[1]));
memset(len,0,sizeof(len));
fa[1]=len[1]=0;
}
void Insert(int c){
int p=last;
int np=++cnt;
num[np]=1;
memset(next[cnt],0,sizeof(next[cnt]));
len[np]=len[p]+1;last=np;
while(p&&!next[p][c]) next[p][c]=np,p=fa[p];
if(!p) fa[np]=1;
else{
int q=next[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else{
int nq=++cnt;
len[nq]=len[p]+1;
memcpy(next[nq],next[q],sizeof next[nq]);
fa[nq]=fa[q]; fa[np]=fa[q]=nq;
while(next[p][c]==q) next[p][c]=nq,p=fa[p];
}
}
}
void build(char *s){
memset(cntA,0,sizeof(cntA));
memset(A,0,sizeof(A));
for(int i=1;i<=cnt;i++) cntA[len[i]]++;
for(int i=1;i<=cnt;i++) cntA[i]+=cntA[i-1];
for(int i=cnt;i;i--) A[cntA[len[i]]--] =i;
int tmp=1;
int sz=strlen(s);
for(int i=0;i<sz;i++){
num[tmp=next[tmp][s[i]-'a']]=1;
}
for(int i=cnt;i;i--){
int x=A[i];
num[fa[x]]+=num[x];
}
for(int i=1;i<=cnt;i++){
int x=A[i];
sum[x]=sum[fa[x]]+num[x]*(len[x]-len[fa[x]]);
}
}
int query(char *s){
int sz=strlen(s),now=1,Len=0;
int res=0;
for(int i=0;i<sz;i++){
int c=s[i]-'a';
if(next[now][c]){
Len++;
now=next[now][c];
}
else{
while(now&&!next[now][c]) now=fa[now];
if(now){
Len=len[now]+1;
now=next[now][c];
}
else{
Len=0,now=1;
}
}
res+=sum[fa[now]]+num[now]*(Len-len[fa[now]]);
}
return res;
}
}sam;
ll ans[maxn];
char tmp1[maxn];
char tmp2[maxn];
int main()
{
//freopen("in.txt","r",stdin);
scanf("%s",str);
int t;
scanf("%d",&t);
int len=strlen(str);
for(int i=0;i<len-1;i++){
memset(tmp1,0,sizeof(tmp1));
memset(tmp2,0,sizeof(tmp2));
for(int j=0;j<=i;j++) tmp1[j]=str[j];
for(int j=i+1;j<len;j++) tmp2[j-i-1]=str[j];
sam.clear();
int sz=strlen(tmp1);
for(int i=0;i<sz;i++){
sam.Insert(tmp1[i]-'a');
}
sam.build(tmp1);
ans[i+1]=sam.query(tmp2);
}
while(t--){
int n;
scanf("%d",&n);
printf("%lld
",ans[n]);
}
return 0;
}