题意(后缀数组模板题)
给你一个字符串,问该字符串的每一个前缀中分别有多少个不同的子串。
n<=100000
这道题目最简单粗暴的方法是建立sam,然后每个点用一个map保存,统计答案就用len[i]-len[fa[i]]即可。。
考虑后缀数组。将整个数组反过来,那么每次就相当于查询后缀[i,n]中有多少本质不同的子串。那么就可以建出后缀数组,然后新加入一个后缀[p],那么首先需要得到这个后缀中有多少子串是重复的,相当于求这个后缀与后缀集合[p+1,n]的最长lcp。由于已经得到了后缀数组,只需要知道后缀[p]在[p+1,n]中相邻的两个后缀(树状数组维护,非常巧妙,查询前后缀max,并将后缀下标转成前缀),然后利用rmq求任意后缀的lcp即可。
注意开long long。(from lych)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define lowbit(x) (x&(-x))
typedef long long ll;
int s[maxn],c[maxn],t1[maxn * 2],t2[maxn * 2],sa[maxn],rk[maxn],h[maxn];
int n,a[maxn];
int mn[20][maxn * 2],cnt[maxn];
int mx[2][maxn];
ll ans[maxn];
//sa[i]表示排名为i的后缀的位置
//rk[i]表示第i个后缀的排名
//x[i]表示i的排名(第一关键字)
//y[i]表示第二关键字排名为i的后缀的起始位置
//h[i]表示后缀i在排序后与前一位的lcp
//求i,j的lcp是排序后rk[i],rk[j],height的min
void suffix_array(){
int m = n , *x = t1 , *y = t2;
rep(i,0,m) c[i] = 0;
rep(i,0,n - 1) c[x[i] = s[i]]++;
rep(i,1,m) c[i] += c[i - 1];
rep(i,0,n - 1) sa[--c[x[i]]] = i;
// rep(i,1,n) cout<<sa[i - 1]<<" ";
// cout<<endl;;
for (register int k = 1 ; k < n ; k <<= 1){
register int p = 0;
memset(y,0,sizeof(t1));
repd(i,n - 1,n - k) y[p++] = i; //必须倒着for,在后面的位置更小
rep(i,0,n - 1) if ( sa[i] >= k ) y[p++] = sa[i] - k;
rep(i,0,m) c[i] = 0;
rep(i,0,n - 1) c[x[y[i]]]++;
rep(i,1,m) c[i] += c[i - 1];
repd(i,n - 1,0) sa[--c[x[y[i]]]] = y[i];
p = 0 , swap(x,y) , x[sa[0]] = ++p;
rep(i,1,n - 1) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]]) && (y[sa[i] + k] == y[sa[i - 1] + k]) ? p : ++p;
if ( p >= n ) break; //如果当前已经完成排名,则break
m = p;
}
rep(i,0,n - 1) rk[sa[i]] = i;
int k = 0;
rep(i,0,n - 1){
if ( !rk[i] ) continue;
int j = sa[rk[i] - 1];
if ( k ) k--;
while ( s[j + k] == s[i + k] ) k++;
h[rk[i]] = k;
}
/* rep(i,0,n - 1) cout<<sa[i]<<" "<<h[i]<<endl;;
cout<<endl;;
rep(i,0,n - 1) cout<<rk[i]<<" ";
cout<<endl;*/
}
void pre(){
sort(a,a + n);
rep(i,0,n - 1) s[i] = lower_bound(a,a + n,s[i]) - a + 1;
reverse(s,s + n);
}
//求两个后缀的最长公共前缀
//在后缀排序后的h数组中用区间RMQ查最小值
void init(){
int k = 0;
rep(i,0,n){
if ( i > (1 << (k + 1)) ) k++;
cnt[i] = k;
}
suffix_array();
rep(i,0,n - 1) mn[0][i] = h[i];
rep(i,1,17)
rep(j,0,n - 1)
mn[i][j] = min(mn[i - 1][j],mn[i - 1][j + (1 << (i - 1))]);
}
inline int lcp(int x,int y){
if ( y == -1 || y >= n ) return 0;
if ( x > y ) swap(x,y);
x++;
int c = cnt[y - x + 1];
return min(mn[c][x],mn[c][y - (1 << c) + 1]);
}
//==============================================================================
inline int query(int t,int id){
int res = -1;
for (int i = id ; i ; i -= lowbit(i)) res = max(res,mx[t][i]);
return res;
}
inline void insert(int t,int id){
for (int i = id ; i <= n ; i += lowbit(i)) mx[t][i] = max(mx[t][i],id);
}
int main(){
freopen("input.txt","r",stdin);
scanf("%d",&n);
rep(i,0,n - 1) scanf("%d",&s[i]) , a[i] = s[i];
pre();
init();
repd(i,n - 1,0){
int t1 = query(0,rk[i]) - 1 , t2 = n - query(1,n - rk[i] - 1);
ans[i] = ans[i + 1] + (n - i) - max(lcp(rk[i],t1),lcp(rk[i],t2));
insert(0,rk[i] + 1) , insert(1,n - rk[i]);
}
repd(i,n - 1,0) printf("%lld\n",ans[i]);
return 0;
}
暂时不会线性后缀数组,这份代码编译有问题,留坑
//后缀数组 -线性
#include<bits/stdc++.h>
#define N 100020
#define fill_n(a,n,d) memset(a,d,sizeof(a));
namespace SA {
int sa[N], rk[N], ht[N], s[N<<1], t[N<<1], p[N], cnt[N], cur[N];
#define pushS(x) sa[cur[s[x]]--] = x
#define pushL(x) sa[cur[s[x]]++] = x
#define inducedSort(v) fill_n(sa, n, -1); fill_n(cnt, m, 0); \
for (int i = 0; i < n; i++) cnt[s[i]]++; \
for (int i = 1; i < m; i++) cnt[i] += cnt[i-1]; \
for (int i = 0; i < m; i++) cur[i] = cnt[i]-1; \
for (int i = n1-1; ~i; i--) pushS(v[i]); \
for (int i = 1; i < m; i++) cur[i] = cnt[i-1]; \
for (int i = 0; i < n; i++) if (sa[i] > 0 && t[sa[i]-1]) pushL(sa[i]-1); \
for (int i = 0; i < m; i++) cur[i] = cnt[i]-1; \
for (int i = n-1; ~i; i--) if (sa[i] > 0 && !t[sa[i]-1]) pushS(sa[i]-1)
void sais(int n, int m, int *s, int *t, int *p) {
int n1 = t[n-1] = 0, ch = rk[0] = -1, *s1 = s+n;
for (int i = n-2; ~i; i--) t[i] = s[i] == s[i+1] ? t[i+1] : s[i] > s[i+1];
for (int i = 1; i < n; i++) rk[i] = t[i-1] && !t[i] ? (p[n1] = i, n1++) : -1;
inducedSort(p);
for (int i = 0, x, y; i < n; i++) if (~(x = rk[sa[i]])) {
if (ch < 1 || p[x+1] - p[x] != p[y+1] - p[y]) ch++;
else for (int j = p[x], k = p[y]; j <= p[x+1]; j++, k++)
if ((s[j]<<1|t[j]) != (s[k]<<1|t[k])) {
ch++;
break;
}
s1[y = x] = ch;
}
if (ch+1 < n1) sais(n1, ch+1, s1, t+n, p+n1);
else for (int i = 0; i < n1; i++) sa[s1[i]] = i;
for (int i = 0; i < n1; i++) s1[i] = p[sa[i]];
inducedSort(s1);
}
template<typename T>
int mapCharToInt(int n, const T *str) {
int m = *max_element(str, str+n);
fill_n(rk, m+1, 0);
for (int i = 0; i < n; i++) rk[str[i]] = 1;
for (int i = 0; i < m; i++) rk[i+1] += rk[i];
for (int i = 0; i < n; i++) s[i] = rk[str[i]] - 1;
return rk[m];
}
// Ensure that str[n] is the unique lexicographically smallest character in str.
template<typename T>
void suffixArray(int n, const T *str) {
int m = mapCharToInt(++n, str);
sais(n, m, s, t, p);
for (int i = 0; i < n; i++) rk[sa[i]] = i;
for (int i = 0, h = ht[0] = 0; i < n-1; i++) {
int j = sa[rk[i]-1];
while (i+h < n && j+h < n && s[i+h] == s[j+h]) h++;
if (ht[rk[i]] = h) h--;
}
}
};