后缀数组学习小记
1. P4070 [SDOI2016]生成魔咒
姑且先称为 动态找出串中不同本质的串的个数
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF = 0x3f3f3f3f;
const int N = 1e6+10, M = 20000010;
inline ll read(){
char ch = getchar();
ll f = 1 , x = 0;
while(ch > '9' || ch < '0'){
if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
/*
考虑到每添加一个新的就会影响所有的后缀 所以要翻转字符串使得每次添加的都是前缀
每次添加一个新的前缀比较麻烦 所以考虑每次删除一个前缀
删除前缀之后要维护height数组 这里用双向链表维护
注意链表的边界 根据 LCP(i,j) = min(LCP(i,k),LCP(k,j))来维护
*/
int s[N];
int n, m;
int sa[N], x[N], y[N], c[N], height[N],rk[N];
int nxt[N],pre[N];
ll ans[N];
int get(int x)
{
static unordered_map<int,int>hash;
if (hash.count(x) == 0) hash[x] = ++m;
return hash[x];
}
void get_sa()
{
for (int i = 1; i <= n; i++) c[x[i] = s[i]] ++;
for (int i = 2; i <= m; i++) c[i] += c[i-1];
for (int i = n; i; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1)
{
int num = 0;
for (int i = n-k+1; i <= n; i++) y[++num] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > k)
y[++num] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i-1];
for (int i = n; i; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k]) ?num:++num;
if (num == n) break ;
m = num;
}
}
void get_height()
{
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= n; i++)
{
if (rk[i] == 1) continue ;
if (k) k--;
int j = sa[rk[i]-1];
while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;
height[rk[i]] = k;
}
}
int main()
{
n = read();
for (int i = n; i >= 1; i--) s[i] = read(), s[i] = get(s[i]);
get_sa();get_height();
ll res = 0;
for (int i = 1; i <= n; i++)
{
res += n - sa[i] + 1 - height[i];
nxt[i] = i+1,pre[i] = i-1;
}
nxt[0] = 1, pre[n+1] = n;
for (int i = 1; i <= n; i++)
{
ans[i] = res;
int k = rk[i], j = nxt[k];
res -= n - sa[k] + 1 - height[k];
res -= n - sa[j] + 1 - height[j];
height[j] = min(height[j],height[k]);
res += n - sa[j] + 1 - height[j];
nxt[pre[k]] = nxt[k];
pre[nxt[k]] = pre[k];
}
for (int i = n ; i >= 1; i --)
printf("%lld\n", ans[i]);
return 0;
}
/*
求一段字符串的lcp的贡献
height【i】 为 区间最小值就会产生贡献
如果height【i-1】 大于 height【i】
选中这两个后缀的时候产生的贡献是height【i】
L【i】 ~ i 和 i ~ R【i】
运用了 lcp[i,j] 那个min的性质以及对height的理解
*/
int n, m;
char s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];
void get_sa()
{
for (int i = 1; i <= n; i++) c[x[i] = s[i]] ++;
// 第一轮第一关键字 以及种类数
for (int i = 2; i <= m; i++) c[i] += c[i-1];
// 前面有几个比自己小 求个前缀和
for (int i = n; i; i--) sa[c[x[i]]--] = i;
// 倒着标
for (int k = 1; k <= n; k <<= 1)
{
// 倍增 /2/2/2/2
int num = 0;
for (int i = n-k+1; i <= n; i++) y[++num] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > k) // 排名为i的串要大于k个
y[++num] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
//清空计数
for (int i = 1; i <= n; i++) c[x[i]] ++;
for (int i = 2; i <= m; i++) c[i] += c[i-1];
for (int i = n; i; i--) sa[c[x[y[i]]] --] = y[i], y[i] = 0;
// 重复步骤
swap(x,y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k])?num:++num;
if (num == n) break;
m = num;
}
}
void get_height()
{
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= n; i++)
{
if (rk[i] == 1) continue ;
if (k) k--;
int j = sa[rk[i]-1];
while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
height[rk[i]] = k;
}
}
int L[N], R[N];
int stk[N], tp;
int main()
{
scanf("%s", s + 1);
n = strlen(s+1), m = 122;
get_sa();get_height();
ll ans = 1ll*n*(n+1)/2*(n-1);
stk[++tp] = 1;
height[1] = 0;
for (int i = 2; i <= n; i++)
{
while (height[stk[tp]] > height[i]) R[stk[tp--]] = i;
L[i] = stk[tp];
stk[++tp] = i;
}
while (tp) R[stk[tp--]] = n + 1;
for (int i = 2; i <= n; i++)
ans -= 1ll*2*(i-L[i])*(R[i]-i)*height[i];
cout << ans;
return 0;
}
kuangbin专题
int n, m;
int s[N];
int sa[N], x[N],y[N],c[N],rk[N],height[N];
void get_sa()
{
m = 233;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i] = s[i]] ++;
for (int i = 2; i <= m; i++) c[i] += c[i-1];
for (int i = n; i; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k<<=1)
{
int num = 0;
for (int i = n-k+1; i <= n; i++) y[++num] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > k)
y[++num] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i-1];
for (int i = n; i; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x,y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i] + k] == y[sa[i-1]+k])?num:++num;
if (num == n) break ;
m = num;
}
}
void get_height()
{
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= n; i++)
{
if (rk[i] == 1) continue ;
if (k) k--;
int j = sa[rk[i]-1];
while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
height[rk[i]] = k;
}
}
bool check(int x)
{
int mx = sa[1], mn = sa[1];
for (int i = 2; i <= n; i++)
{
if (height[i] >= x - 1)
{
mx = max(mx,sa[i]);
mn = min(mn,sa[i]);
}
else
mx = mn = sa[i];
if (mx - mn >= x) return 1;
}
return 0;
}
int main()
{
while (~scanf("%d",&n) && n != 0)
{
for (int i = 1; i <= n; i++) s[i] = read();
for (int i = n; i >= 1; i--) s[i] -= s[i-1] - 88;
get_sa();get_height();
int l = 0, r = n;
while (l < r)
{
int mid = l+r+1>>1;
if (check(mid))
l = mid;
else
r = mid-1;
}
printf("%d\n", l >= 5?l:0);
}
return 0;
}