牛客多校第一场 B-Suffix Array(后缀数组 好题)

题目连接
题意:
给你一个长度为 n n n的只由‘a’和‘b’组成字符串 ( n < = 1 e 5 ) (n <= 1e5) (n<=1e5),并重新定义了这个字符串的所有后缀的串的值,让你出去重新定义完以后的该字符串的所有后缀的sa,定义如下:当前位置的值为与前一次出现相同字符的位置的距离。
分析:
举个例子,比如 a a b a a b a a a b a aabaabaaaba aabaabaaaba,那么这个字符串的后缀和所对应的值为:
a a b a a b a a a b a 01021321142 a b a a b a a a b a    0021321142 b a a b a a a b a 001321142 a a b a a a b a    01021142 a b a a a b a 0021142 b a a a b a    001142 a a a b a 01102 a a b a    0102 a b a 002 b a    00 a 0 aabaabaaaba \qquad \qquad 01021321142 \\abaabaaaba \qquad \qquad \ \ 0021321142 \\baabaaaba \qquad \qquad \quad 001321142 \\aabaaaba \qquad \qquad \quad \ \ 01021142 \\abaaaba \qquad \qquad \quad \quad0021142 \\baaaba\qquad \qquad \quad \quad \ \ 001142 \\aaaba\qquad \qquad \quad \quad \quad 01102 \\aaba \qquad \qquad\qquad\quad \ \ 0102 \\aba\qquad \qquad\qquad \qquad002 \\ba \qquad \qquad\qquad \qquad \ \ 00 \\a \qquad \qquad\qquad \qquad \quad 0 aabaabaaaba01021321142abaabaaaba  0021321142baabaaaba001321142aabaaaba  01021142abaaaba0021142baaaba  001142aaaba01102aaba  0102aba002ba  00a0

乍一看啥都没看出来,比赛的时候也对这个分析了半天一无所获。看了题解才理解,太妙了。
首先对这一串数字进行分组,第一组是该字符串从左到右直到a,b都出现过一次就停止。即出现两个0
那么上面的就是 010 , 00 , 00 , 010 , 00 , 00 , 0110 , 010 , 00 , 00 , 0 010,00,00,010,00,00,0110,010,00,00,0 01000000100000011001000000
然后你会发现一个神奇的事情就是,字符串越长,这个字符的字典序就越大!那么第一部分就解决了。

第二组就是剩下的组合,那么就是:
21321142 , 21321142 , 1321142 , 21142 , 1142 , 42 , 2 , 2 , 2 , n u l l , n u l l 21321142,21321142,1321142,21142,1142,42,2,2,2,null,null 21321142,21321142,1321142,21142,1142,42,2,2,2,null,null
然后就可以发现这些都是原始字符串的第 n − l e n + 1 n-len+1 nlen+1个后缀,因此我们对原始数组求一次sa即可得到后面一部分字符串的rk。

于是对原字符串的排序就说一个简单的比较了,先按第一关键字小的,相同就按第二关键字小的,然后输出就好了。

代码:

#include<bits/stdc++.h>
#define mes(a, b)  memset(a, b, sizeof a)
#define pb push_back
typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e5 + 20;
const int    maxm = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;
char s[maxn];
int b[maxn];
int rk[maxn],tp[maxn],sa[maxn],tax[maxn];
int n,m;
void Qsort(){
	for(int i = 0;i <= m;i++) tax[i] = 0;
	for(int i = 1;i <= n;i++) tax[rk[i]]++;
	for(int i = 1;i <= m;i++) tax[i] += tax[i -1];
	for(int i = n;i >= 1;i--) sa[tax[rk[tp[i]]]--] = tp[i];
}
void GetSa(){
	m = n;
	for(int i = 1;i <= n;i++) tp[i] = i,rk[i] = b[i];
	Qsort();
	int p = 0;
	for(int w = 1;p < n;w <<= 1){
		int tot = 0;
		for(int i = 1;i <= w;i++) tp[++tot] = n - w + i;
		for(int i = 1;i <= n;i++) if(sa[i] > w)  tp[++tot] = sa[i] - w;
		Qsort();
		for(int i = 1;i <= n;i++) tp[i] = rk[i];
		rk[sa[1]] = p = 1;
		for(int i = 2;i <= n;i++){
			rk[sa[i]] = (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w]) ? p : ++p;
		}
		m = p;
	}
}
struct node{
	int x,y;
	bool operator<(const node &a)const{
		if(y - x == a.y - a.x) return rk[y + 1] < rk[a.y + 1];
		return y - x < a.y - a.x;
	}
}a[maxn];
int main(){
	while(~scanf("%d",&n)){
		scanf("%s",s + 1);
		int x = -1,y = -1;
		for(int i = 1;i <= n;i++){
			b[i] = 0;
			if(s[i] == 'a'){
				if(x != -1) b[i] = i - x;
				x = i;
			}
			else if(s[i] == 'b'){
				if(y != -1) b[i] = i - y;
				y = i;
			}
			b[i]++;
		}
		GetSa();
    	rk[n + 2] = -2;
		rk[n + 1] = -1;
		x = y = n + 1;
		for(int i = n;i >= 1;i--){
			if(s[i] == 'a'){
				a[i] = {i,y};x = i;
			}
			else if(s[i] == 'b'){
				a[i] = {i,x};y = i;
			}
		}
		sort(a + 1,a + 1 + n);
		for(int i = 1;i <= n;i++) printf("%d ",a[i].x);
		puts("");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值