(很神仙的题啊 爆栈了 手动扩栈才过
题解:我们考虑题目实质上形成一颗trie树 查询的是trie树上每个节点到根路径上形成的字符串的子串中不同子串的个数 我们考虑每个点的价值=父亲节点的价值+这个点后缀不同子串的个数 那么我们建广义的后缀自动机在trie树上转移 然后查询当前节点在parent树中到根链的并集即可 那么我们考虑用set来维护树链的并 然后统计答案即可
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=2e5+10;
const double eps=1e-8;
#define ll long long
using namespace std;
// int size = 256 << 20; // 256MB
// char *pp = (char*)malloc(size) + size;
// __asm__("movl %0, %%esp\n" :: "r"(pp));
struct edge{int t;edge*next;}e[MAXN<<1],*h[MAXN<<1],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
typedef struct node{
int f,a[26];
vector<int>vec;
}node;
node d[MAXN];
int rt,cnt;char str[MAXN];
void newnode(int &x,int pre){
x=++cnt;d[x].vec.clear();d[x].f=pre;
for(int i=0;i<26;i++)d[x].a[i]=0;
}
int cur,rt1,dis[MAXN<<1],fa[MAXN<<1][21],cnt1,ch[MAXN<<1][26],dep[MAXN<<1];
int pos[MAXN];
ll dist[MAXN<<1],ans[MAXN];
int built(int x){
int last=cur;cur=++cnt1;dis[cur]=dis[last]+1;int pp=last;
for(;pp&&!ch[pp][x];pp=fa[pp][0])ch[pp][x]=cur;
if(!pp)fa[cur][0]=rt1;
else{
int q=ch[pp][x];
if(dis[q]==dis[pp]+1)fa[cur][0]=q;
else{
int nt=++cnt1;dis[nt]=dis[pp]+1;
memcpy(ch[nt],ch[q],sizeof(ch[q]));
fa[nt][0]=fa[q][0];fa[q][0]=fa[cur][0]=nt;
for(;ch[pp][x]==q;pp=fa[pp][0])ch[pp][x]=nt;
}
}
return cur;
}
void dfs(int x){
for(int i=0;i<26;i++){
if(!d[x].a[i])continue;
cur=pos[x];pos[d[x].a[i]]=built(i);
dfs(d[x].a[i]);
}
}
int p[MAXN<<1],fp[MAXN<<1],cnt2;
set<int>s;
set<int>::iterator ite,ip;
void dfs1(int x,int deep){
dep[x]=deep+1;p[x]=++cnt2;fp[p[x]]=x;
for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
link(x){
dist[j->t]=dist[x]+dis[j->t]-dis[x];
dfs1(j->t,deep+1);
}
}
int Lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int tmp=dep[u]-dep[v];
for(int i=0;i<=20;i++)if(tmp&(1<<i))u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--){
if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
void solve(int x,ll ans1){
for(int i=0;i<26;i++){
if(!d[x].a[i])continue;
int xx=0,yy=0;ll t=ans1;
ite=s.lower_bound(p[pos[d[x].a[i]]]);
//printf("%d=====\n",s.size());
t+=dist[pos[d[x].a[i]]];
if(ite!=s.begin())ip=ite,ip--,xx=fp[(*ip)],t-=dist[Lca(xx,pos[d[x].a[i]])];
if(ite!=s.end())ip=ite,yy=fp[(*ip)],t-=dist[Lca(yy,pos[d[x].a[i]])];
if(xx&&yy)t+=dist[Lca(xx,yy)];
for(int j=0;j<d[d[x].a[i]].vec.size();j++)ans[d[d[x].a[i]].vec[j]]=t;
s.insert(p[pos[d[x].a[i]]]);
solve(d[x].a[i],t);
}
s.erase(p[pos[x]]);
}
int main(){
rt=cnt=0;cnt1=0;cnt2=0;
scanf("%s",str+1);int len=strlen(str+1);newnode(rt,0);
int temp=rt;
for(int i=1;i<=len;i++){
if(str[i]=='-')temp=d[temp].f,d[temp].vec.pb(i);
else{
int t=str[i]-'a';
if(!d[temp].a[t]) newnode(d[temp].a[t],temp);
temp=d[temp].a[t];
d[temp].vec.pb(i);
}
}
//cout<<cnt<<endl;
//cout<<"sb"<<endl;
pos[rt]=1;cnt1=rt1=1;dfs(rt);
//cout<<cnt1<<endl;
//cout<<"sb"<<endl;
for(int i=1;i<=cnt1;i++)add(fa[i][0],i);
dfs1(rt1,0);
//cout<<"sb"<<endl;
solve(rt,0);
//for(int i=1;i<=len;i++)
for(int i=1;i<=len;i++)printf("%lld\n",ans[i]);
}
5084: hashit
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 223 Solved: 94
[Submit][Status][Discuss]
Description
你有一个字符串S,一开始为空串,要求支持两种操作
在S后面加入字母C
删除S最后一个字母
问每次操作后S有多少个两两不同的连续子串
Input
一行一个字符串Q,表示对S的操作
如果第i个字母是小写字母c,表示第一种加字母c的操作
如果为-表示删除操作,保证所有删除操作前S都非空
|Q|<=10^5
Output
输出|Q|行,第i行表示i个操作之后S内有多少个不同子串
Sample Input
aba-caba
Sample Output
1
3
5
3
6
9
12
17
3
5
3
6
9
12
17