题意:
一个初始为空的串,每次在末尾插入一个字符,每次插入后问字符串中本质不同的子串的个数。字符值域1e9。
知识点:
后缀自动机
解法:
因为SAM本来就是支持动态末尾插入的在线结构,而且SAM有一个性质是本质不同子串个数等于所有点的len减去parent树上的父亲点的len,所以每次插入改ans即可。
备注:
因为值域很大,所以改用map维护转移的字符。
另外好像后缀数组也可以做,加点什么平衡树也可以做,不过我这种方法应该代码最好写、最短(里面有一些map的find、insert操作可以在访问空节点的时候变快)。
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=200010;
ll ans;
int n,m,tot,lst;
struct sam
{
map<int,int>son;
int fa,len;
}a[maxn];
int read()
{
int x=0;
char c=getchar();
while (c<48||c>57)
c=getchar();
while (c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void sam_insert(int c)
{
int p=lst,u=(++tot);
lst=u;
a[u].len=a[p].len+1;
for (;p&&a[p].son.find(c)==a[p].son.end();p=a[p].fa)
a[p].son.insert(make_pair(c,u));
if (!p)
a[u].fa=1;
else
{
int v=a[p].son[c];
if (a[v].len==a[p].len+1)
a[u].fa=v;
else
{
int w=(++tot);
a[w]=a[v];
a[w].len=a[p].len+1;
a[v].fa=a[u].fa=w;
for (;p&&a[p].son[c]==v;p=a[p].fa)
a[p].son[c]=w;
}
}
ans+=a[u].len-a[a[u].fa].len;
}
int main()
{
int x;
tot=lst=1;
n=read();
while (n--)
{
x=read();
sam_insert(x);
printf("%lld
",ans);
}
return 0;
}