题目
内容
对于原串的每个前缀,求有多少个不同的子串.
后缀数组
对于长度为 的串,子串个数为
把字符串翻转,从前往后插入字符,用set查询插入字符的前一个字符pre和后一个字符next
PS: 需要离散化一下
#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxbit=20;
struct SuffixArray
{
int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], st[maxbit][maxn], N;
bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
void build(int *r, int n, int m)
{
N=n;
n++;
int i, j, k=0, p, *x=wa, *y=wb, *t;
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[x[i]=r[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<n;i++)wv[i]=x[y[i]];
for(i=0;i<m;i++)ws[i]=0;
for(i=0;i<n;i++)ws[wv[i]]++;
for(i=1;i<m;i++)ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
for(i=0;i<n;i++)rank[sa[i]]=i;
for(i=0;i<n-1;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void build_st() //st表
{
int i, k;
for(i=1;i<=N;i++)st[0][i]=height[i];
for(k=1;k<=maxbit;k++)
for(i=1;i+(1<<k)-1<=N;i++)
st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
}
int lcp(int x, int y) //最长公共前缀
{
int l=x, r=y;
if(l>r)swap(l,r);
if(l==r)return N-sa[l];
int t=log2(r-l);
return min(st[t][l+1],st[t][r-(1<<t)+1]);
}
}SA;
int Hash[maxn],num,strcp[maxn],a[maxn];
int get_hash(int x)
{
return lower_bound(Hash+1,Hash+num,x)-Hash;
}
set<int> s;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
int n;
cin>>n;
rep(i,0,n-1)
{
cin>>a[i];
Hash[i+1]=a[i];
}
rep(i,0,n-1) strcp[i]=a[n-1-i];
sort(Hash+1,Hash+n+1);
num=unique(Hash+1,Hash+n+1)-Hash;
rep(i,0,n-1) strcp[i]=get_hash(strcp[i]);
strcp[n]=0;
SA.build(strcp,n,n+100);
SA.build_st();
ll sum=0,now=1;
per(i,n-1,0)
{
auto it=s.lower_bound(SA.rank[i]);
int l,r;
if(it==s.end()) r=0;
else r=*it;
if(it==s.begin()) l=0;
else
{
it--;
l=*it;
}
if(l&&r) sum-=SA.lcp(l,r);
if(l) sum+=SA.lcp(l,SA.rank[i]);
if(r) sum+=SA.lcp(r,SA.rank[i]);
s.insert(SA.rank[i]);
cout<<now*(now+1)/2-sum<<'\n';
now++;
}
return 0;
}
后缀自动机
SAM裸题
由于字符集过大,要用map维护
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define debug(x) cerr<<#x<<' '<<x<<'\n'
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
ll ans=0;
struct sam {
map<int,int> t[maxn<<1];
int len[maxn<<1] = {-1}, fa[maxn<<1], sz = 2, last = 1;
void ins(int ch) {
int p = last, np = last = sz++;
len[np] = len[p] + 1;
for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np;
if (!p) {
fa[np] = 1;
ans+=len[np]-len[fa[np]];
return;
}
int q = t[p][ch];
if (len[p] + 1 == len[q]) fa[np] = q;
else {
int nq = sz++; len[nq] = len[p] + 1;
ans-=len[q]-len[fa[q]];
t[nq]=t[q];
fa[nq] = fa[q];
fa[np] = fa[q] = nq;
ans+=len[nq]-len[fa[nq]];
ans+=len[q]-len[fa[q]];
for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq;
}
ans+=len[np]-len[fa[np]];
}
}sam;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
int n;
cin>>n;
rep(i,1,n) {
int x;
cin>>x;
sam.ins(x);
cout<<ans<<'\n';
}
return 0;
}