后缀数组填坑记(待续)

tonyfang posted @ 2016年3月06日 21:38 in 随笔 with tags c++ OI , 1170 阅读

后缀数组从入门到精通

1. 约定

suffix(i):以i为开头的后缀;

LCP 最长公共前缀 longest common prefix

2. 数组定义

sa[i]: 排名i的后缀的开始下标

rank[i]: 开始下标为i的后缀中名次。

也就是说,sa[rank[i]]=i,rank[sa[i]]=i。

height[i]: LCP(suffix(sa[i]), suffix(sa[i-1]))

通俗的来说,排第i名的和排第i-1名的LCP。

h[i] = height[rank[i]]

也就是说,suffix(i)和比他前一名的后缀的LCP。

3. sa[]数组的求法——倍增$O(nlog_{2}n)$。

对于长度为$2^p$的后缀,我们将$i$和$i+2^{p-1}$组合,用基数排序即可获得新sa值。

如果用sort,复杂度变成$O(nlog_{2}^{2}n)$

4. rank[]的求法——性质$O(n)$

根据rank[sa[i]]=i,可以求出rank[]

5. height[]的求法——h[]数组的性质

根据论文证明,$h[i] \geq h[i-1]-1$,然后我们即可$O(n)$求出height[]

6. 经典题目:

(1) 后缀的LCP问题

很明显,

LCP(suffix(i),suffix(j))=min{height[rank[i+1],rank[i+2],...,rank[j]}

用RMQ即可搞定。

(2) 可重叠最长重复子串

求height的最大值即可。

(3) 不可重叠的最长重复子串

二分长度l,对后缀分组后查询距离即可。

(4) 不可重叠的最长k重复子串

同(3),加判断要大等k即可。

(5) 不相同的子串个数

子串一定是prefix(suffix(i)),那么如果按照sa[1],...sa[n]插入,每插入一个,贡献(n-sa[i]+1-height[i])个子串,累加即可。

(6) 最长回文子串

复制一遍放在后面然后就是一个LCP。

具体可以看lhq的论文噢~

【BZOJ 1031 JSOI2007 字符加密】

把字符串复制两遍后搞rank值即可。

 

# include <stdio.h>
# include <string.h>
using namespace std;

const int M=600010;
int p[M],cnt[M],rank[M],rank_t[M],sa[M],sa_t[M],rank_t2[M],s[M],t[M];
char str[M];


inline bool cmp(int *s, int a, int b, int l) {
	return s[a]==s[b] && s[a+l]==s[b+l];
}

void getsa(int n,int m) {
	memset(cnt, 0, sizeof(cnt));
	for (int i=0; i<n; ++i) ++cnt[rank_t[i] = s[i]];
	for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
	for (int i=n-1; i>=0; --i) sa[--cnt[s[i]]] = i;
	for (int j=1, p=1; p<n; j<<=1, m=p) {
		p=0;
		for (int i=n-j; i<n; ++i) sa_t[p++]=i;
		for (int i=0; i<n; ++i) if(sa[i] >= j) sa_t[p++] = sa[i]-j;
		memset(cnt, 0, sizeof(cnt));
		for (int i=0; i<n; ++i) ++cnt[rank_t2[i] = rank_t[sa_t[i]]];
		for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
		for (int i=n-1; i>=0; --i) sa[--cnt[rank_t2[i]]] = sa_t[i], t[i] = rank_t[i];
		rank_t[sa[0]] = 0;
		p=1;
		for (int i=1; i<n; ++i) 
			rank_t[sa[i]] = cmp(t, sa[i], sa[i-1], j) ? p-1 : p++;
	}
}

int main() {
	scanf("%s",str);
	int n = strlen(str);
	for (int i=0; i<n; ++i) s[i]=s[i+n]=str[i];
	s[n+n-1] = 0;
	getsa(n*2, 300);
	for (int i=0; i<n*2; ++i) rank[sa[i]] = i;
	for (int i=0; i<n; ++i) p[rank[i]]=s[i+n-1];
	for (int i=0; i<n*2; ++i) 
		if(p[i]) 
			printf("%c", (char)p[i]);
	return 0;
}

 

【BZOJ 2251 外星联络】

求出h的值后,我们就可以搞出答案啦~

当然这题更快的是用trie。

 

# include <stdio.h>
# include <string.h>
using namespace std;

const int M=600010;
int sa[M],sa_t[M],rank[M],rank_t[M],rank_t2[M];
int cnt[M],s[M],t[M],h[M],n,ans[M],ansn=0;
char str[M];

inline bool cmp(int *s,int a,int b,int l) {
	return s[a]==s[b] && s[a+l] == s[b+l];
}

inline bool getsa(int n,int m) {
	memset(cnt, 0, sizeof(cnt));
	for (int i=0; i<n; ++i) ++cnt[rank_t[i] = s[i]];
	for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
	for (int i=n-1; i>=0; --i) sa[--cnt[s[i]]] = i;
	for (int j=1, p=1; p<n; j<<=1, m=p) {
		p=0;
		for (int i=n-j; i<n; ++i) sa_t[p++] = i;
		for (int i=0; i<n; ++i) if(sa[i] >= j) sa_t[p++] = sa[i] - j;
		memset(cnt, 0, sizeof(cnt));
		for (int i=0; i<n; ++i) ++cnt[rank_t2[i] = rank_t[sa_t[i]]];
		for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
		for (int i=n-1; i>=0; --i) sa[--cnt[rank_t2[i]]] = sa_t[i], t[i] = rank_t[i];
		rank_t[sa[0]] = 0;
		p=1;
		for (int i=1; i<n; ++i)
			rank_t[sa[i]] = cmp(t, sa[i], sa[i-1], j) ? p-1 : p++;
	}
	for (int i=0; i<n; ++i) rank[sa[i]] = i;
}

inline void calheight(int n) {
	for (int i=0, k=0; i<n; h[rank[i++]] = k) 
		if(rank[i]) {
			if(k>0) --k;
			for (;s[i+k] == s[sa[rank[i]-1] + k]; ++k);
		} else k=0;
}


int main() {
	scanf("%d", &n);
	scanf("%s", str);
	for (int i=0; i<n; ++i) s[i]=str[i]-'0'+1;
	s[n]=0;
	getsa(n,3);
	calheight(n);
	
	for (int i=0; i<n; ++i) {
		int k=i+1;
		ansn=0;
		for (int j=n-sa[i]; j>h[i]; --j) {
			while(h[k] >= j) k++;
			if (k != i+1) ans[++ansn] = k - i;
		}
		for (int i=ansn; i>=1; --i) printf("%d\n", ans[i]);
	}
	
	return 0;
}

 

【BZOJ 3172 单词】

 

# include <stdio.h>
# include <string.h>

using namespace std;

const int M=1600010;
int sa[M], sa_t[M], rank[M], rank_t[M], rank_t2[M], t[M], s[M], cnt[M];
int start[M], length[M], h[M];
char str[M];

inline bool cmp(int *t, int a, int b, int l) {
	return t[a] == t[b] && t[a+l] == t[b+l];
}

inline void getsa(int n, int m) {
	memset(cnt, 0, sizeof(cnt));
	for (int i=0; i<n; ++i) ++cnt[rank_t[i] = s[i]];
	for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
	for (int i=n-1; i>=0; --i) sa[--cnt[s[i]]] = i;
	for (int j=1, p=1; p<n; j<<=1, m=p) {
		p=0;
		for (int i=n-j; i<n; ++i) sa_t[p++] = i;
		for (int i=0; i<n; ++i) if(sa[i] >= j) sa_t[p++] = sa[i] - j;
		memset(cnt, 0, sizeof(cnt));
		for (int i=0; i<n; ++i) ++cnt[rank_t2[i] = rank_t[sa_t[i]]];
		for (int i=1; i<m; ++i) cnt[i] += cnt[i-1];
		for (int i=n-1; i>=0; --i) sa[--cnt[rank_t2[i]]] = sa_t[i], t[i] = rank_t[i];
		rank_t[sa[0]]=0;
		p=1;
		for (int i=1; i<n; ++i)
			rank_t[sa[i]] = cmp(t, sa[i], sa[i-1], j) ? p-1 : p++;
	}
	for (int i=0; i<n; ++i) rank[sa[i]] = i;
}
inline void geth(int n) {
	for (int i=0, k=0; i<n; h[rank[i++]] = k) {
		if(rank[i]) {
			if(k>0) --k;
			for (; s[i+k] == s[sa[rank[i] - 1] + k]; ++k);
		} else k=0;
	}
}

int n;

int main() {
	scanf("%d", &n);
	int now = 0;
	for (int i=0; i<n; ++i) {
		scanf("%s", str + now);
		start[i] = now;
		now = strlen(str);
		length[i] = now - start[i];
		str[now++] = ' ';
	}
	for (int i=0; i<now; ++i) s[i] = str[i];
	getsa(now, 150);
	geth(now);
	for (int i=0; i<n; ++i) {
		int j, from = rank[start[i]], len = length[i], left, right;
		j = from; for (; j && h[j] >= len; --j);
		left = j;
		j = from+1; for (; j<now && h[j] >= len; ++j);
		right = j;
		printf("%d\n", right - left);
	}
	return 0;
}

 

 

 

 

 
dpost.in 说:
2023年6月18日 15:59

dpost is an initiative of professional writers who have banded together to provide devoted news coverage of current events in India. Our team is made up of professional writers and citizen journalists with a wide range of dpost.in journalism interests who are passionate about reporting Education Updates with transparency in the general public interest.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter