后缀数组学习小记

后缀数组学习小记

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;
}

P4248 [AHOI2013]差异


/*
	求一段字符串的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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43563956/article/details/109807248