后缀数组+RMQ

源处:http://www.notonlysuccess.com/index.php/sa/

 

 

//rank从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//high从2开始,因为表示的是sa[i-1]和sa[i]的LCP
#define M 220000
int rk[M],sa[M],X[M],Y[M],high[M],init[M];
int buc[M];
void calhigh(int n) {
	int i , j , k = 0;
	for(i = 1 ; i <= n ; i++) rk[sa[i]] = i;
	for(i = 0 ; i < n ; high[rk[i++]] = k)
		for(k?k--:0 , j = sa[rk[i]-1] ; init[i+k] == init[j+k] ; k++);
}
bool cmp(int *r,int a,int b,int l) {
	return (r[a] == r[b] && r[a+l] == r[b+l]);
}
void suffix(int n,int m = 128) {
	int i , l , p , *x = X , *y = Y;
	for(i = 0 ; i < m ; i ++) buc[i] = 0;
	for(i = 0 ; i < n ; i ++) buc[ x[i] = init[i]  ] ++;
	for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
	for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
	for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
		p = 0;
		for(i = n-l ; i < n ; i ++) y[p++] = i;
		for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
		for(i = 0 ; i < m ; i ++) buc[i] = 0;
		for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
		for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
		for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
		for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
			x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
	}
	calhigh(n-1);//后缀数组关键是求出high,所以求sa的时候顺便把rk和high求出来
}
 
 
//当需要反复询问两个后缀的最长公共前缀时用到RMQ
int Log[M];
int best[20][M];
void initRMQ(int n) {//初始化RMQ
	for(int i = 1; i <= n ; i ++) best[0][i] = high[i];
	for(int i = 1; i <= Log[n] ; i ++) {
		int limit = n - (1<<i) + 1;
		for(int j = 1; j <= limit ; j ++) {
			best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]); 
		}
	}
}
int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
	a = rank[a];    b = rank[b];
	if(a > b) swap(a,b);
	a ++;
	int t = Log[b - a + 1];
	return min(best[t][a] , best[t][b - (1<<t) + 1]);
}
 
 
int main() {
	//预处理每个数字的Log值,常数优化,用于RMQ
	Log[0] = -1;
	for(int i = 1; i <= M ; i ++) {
		Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
	}
	//*******************************************
	//	n为数组长度,下标0开始
	//	将初始数据,保存在init里,并且保证每个数字都比0大
	//	m = max{ init[i] } + 1
	//	一般情况下大多是字符操作,所以128足够了
	//*******************************************
	init[n] = 0;
	suffix(n+1,m);
 
	initRMQ(n);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值