POJ 3581 启发:后缀数组求最小表示

http://poj.org/problem?id=3581

1.第一段是显然的,reverse原数组然后求后缀数组取sa[0]即可……当然要注意每段不能为空这个特点

2.在求第二段的时候就会遇到这道题的难点,如果单纯求后缀数组取sa[0],很容易举出反例

7

10 0 2 2 2 2 3

或者

8

10 0 3 1 2 3 1 5

后缀数组在比较大小的时候 是默认"2" 比 "222"小的,但是当其后面还要接其它字符串的时候,相对大小就发生了改变。

有string a,b,c;

a < b 推导不出 a+c < b+c               Terrible!

3.看到Discuss里有人说用最小表示法能做,我表示已经忘记最小表示法怎么写了,觉得那东西很难派上用场也懒得再学……但是思考:用后缀数组怎么求解最小表示?



解决方法:取b = a+a;  b的前a.size()个后缀中最小的那个后缀的前a.size()位

//如果要求高一些,像"bababa"这样有循环节的字符串要求起始下标最小的最小表示。假设你找到的最小的那个后缀是sa[i] 那要输出的结果就是与它LCP >= a.size()的最后一个(sa数组中)。下面这份代码需要改一些小细节才能实现这个功能,具体见下一篇博客。



4.回过头来求第二段,方法和上面的几乎是一样的,只是先把a取个反而已

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cctype>
#include <cmath>
#include <vector>
#include <sstream>
#include <bitset>
#include <deque>
#include <iomanip>
using namespace std;
#define pr(x) cout << #x << " = " << x << endl;
#define bug cout << "bugbug" << endl;
#define ppr(x, y) printf("(%d, %d)\n", x, y);
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n---------------")

typedef long long ll;
typedef double DBL;
typedef pair<int, int> P;
typedef unsigned int uint;
const int MOD = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 4e5 + 4;
const int maxm = 1e3 + 4;
const double pi = acos(-1.0);

int a[maxn], b[maxn];
vector<int> v;
int Getid(int num){
	return lower_bound(v.begin(), v.end(), num) - v.begin(); 
}
struct SuffixArray{
	int n, t[maxn << 1], t2[maxn << 1], c[maxn], sa[maxn], Rank[maxn], Height[maxn];
	int s[maxn];
	void build_sa(int m){
		memset(t, 0, sizeof t);
		memset(t2, 0, sizeof t2);
		int i, *x = t, *y = t2;
		for (i = 0; i < m; ++i) c[i] = 0;
		for (i = 0; i < n; ++i) c[x[i] = s[i]]++;
		for (i = 1; i < m; ++i) c[i] += c[i-1];
		for (i = n-1; i >= 0; --i) sa[--c[x[i]]] = i;
		for (int k = 1; k < n; k *= 2){
			int p = 0;
			for (i = n - k; i < n; ++i) y[p++] = i;
			for (i = 0; i < n; ++i)
				if (sa[i] >= k) y[p++] = sa[i] - k;
			for (i = 0; i < m; ++i) c[i] = 0;
			for (i = 0; i < n; ++i) c[x[y[i]]]++;
			for (i = 1; i < m; ++i) c[i] += c[i-1];
			for (i = n-1; i >= 0; --i) sa[--c[x[y[i]]]] = y[i];
			swap(x, y);
			p = 1; x[sa[0]] = 0;
			for (i = 1; i < n; ++i)
				x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1 : p++;
			if (p >= n) break;
			m = p; 
		}
		return;
	}
	void RankAndHeight(){
		for (int i = 0; i < n; ++i) Rank[sa[i]] = i;
		int k = 0;
		Height[sa[0]] = 0;
		for (int i = 1; i < n; ++i){
			if (k) k--;
			if (Rank[i] == 0) continue;
			int *p = s + i, *q = s + sa[Rank[i]-1];
			while(p[k] == q[k]) k++;
			Height[i] = k;
		}
		return;
	}
}SA; 

int main(){
//必须编译过才能交
	int ik, i, j, k, kase;
	int n;
	scanf("%d", &n);
	for (i = 0; i < n; ++i) scanf("%d", a+i), v.push_back(a[i]);
	sort(v.begin(), v.end());
	unique(v.begin(), v.end());
	for (i = 0; i < n; ++i) a[i] = Getid(a[i]);
	reverse(a, a+n-2);
	memcpy(SA.s, a, sizeof a);
	SA.n = n - 2;
	SA.build_sa(n);
	reverse(a, a+n-2);
	// 0 1 2 3 4 5 6   -> 6 5 4 3 2 1 0  sa[0] 对应的是 n-2 n = 9 n - 3 - sa[0]
	int Cut = n - 3 - SA.sa[0];
	reverse(a, a+Cut+1);
	
	
	memcpy(b, a, n*4);
	reverse(a+Cut+1, a+n-1);
	memcpy(SA.s, a+Cut+1, (n-2-Cut)*4);
	memcpy(a, b, n*4);
	reverse(a+Cut+1, a+n);
	memcpy(SA.s+n-2-Cut, a+Cut+1, n-Cut-1);
	SA.n = n - 2 - Cut + n - Cut - 1;
	SA.build_sa(n);
	int Cut2;
	for (i = 0; i < SA.n; ++i)
		if (SA.sa[i] < n - 2 - Cut){
			Cut2 = n - 3 - Cut - SA.sa[i] + Cut + 1 ;
			break;
		}
	memcpy(a, b, n*4);
	reverse(a+Cut+1, a+Cut2+1);
	reverse(a+Cut2+1, a+n); 
	
	
	
	
	for (i = 0; i < n; ++i) printf("%d\n", v[a[i]]);
	return 0;
}
/*
3 1 2 3 1 5


5 1 3 2 1 3 2 1 3 2 5 1 3 2 1 3 2 1 
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值