HDU 5008 Boring String Problem(西安网络赛B题)

HDU 5008 Boring String Problem

题目链接

思路:构造后缀数组,利用height的数组能预处理出每个字典序开始的前缀和有多少个(其实就是为了去除重复串),然后每次二分查找相应位置,然后在往前往后找一下sa[i]最小的

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int MAXLEN = 100005;

struct Suffix {

	char str[MAXLEN];
	int s[MAXLEN];
	int sa[MAXLEN], t[MAXLEN], t2[MAXLEN], c[MAXLEN], n;
	int rank[MAXLEN], height[MAXLEN];
	ll sum[MAXLEN];

	void build_sa(int m) {
		n++;
		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 <<= 1) {
			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 = 0; 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 - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) ? p - 1 : p++;
			if (p >= n) break;
			m = p;
		}
		n--;
	}

	void getHeight() {
		int i, j, k = 0;
		for (i = 1; i <= n; i++) rank[sa[i]] = i;
		for (i = 0; i < n; i++) {
			if (k) k--;
			int j = sa[rank[i] - 1];
			while (s[i + k] == s[j + k]) k++;
			height[rank[i]] = k;
		}
	}

	void getsum() {
		for (int i = 1; i <= n; i++)
			sum[i] = sum[i - 1] + n - sa[i] - height[i];
	}

	void init() {
		n = strlen(str);
		for (int i = 0; i < n; i++)
			s[i] = str[i] - 'a' + 1;
		s[n] = 0;
		build_sa(27);
		getHeight();
		getsum();
	}

	void query(ll &ls, ll &rs, ll k) {
		int u = lower_bound(sum + 1, sum + n + 1, k) - sum;
		if (u == n + 1) {
			ls = 0; rs = 0;
			printf("%I64d %I64d\n", ls, rs);
			return;
		}
		int len = k - sum[u - 1] + height[u];
		int st = sa[u];
		for (int i = u; i > 1; i--) {
			if (height[i] < len) break;
			st = min(st, sa[i - 1]);
		}
		for (int i = u + 1; i <= n; i++) {
			if (height[i] < len) break;
			st = min(st, sa[i]);
		}
		ls = st + 1; rs = st + len;
		printf("%I64d %I64d\n", ls, rs);
	}

	void solve() {
		init();
		ll l = 0, r = 0, v;
		int q;
		scanf("%d", &q);
		while (q--) {
			scanf("%I64d", &v);
			ll k = (l^r^v) + 1;
			query(l, r, k);
		}
	}
} gao;

int main() {
	while (~scanf("%s", gao.str)) {
		gao.solve();
	}
	return 0;
}
时间: 2024-12-28 09:59:11

HDU 5008 Boring String Problem(西安网络赛B题)的相关文章

hdu 5008 Boring String Problem(后缀数组)

题目链接:hdu 5008 Boring String Problem 题目大意:给定一个字符串,初始状态l,r为0,每次询问子串中字典序第l^r^v+1的子串区间,对于重复的输出下标小的. 解题思路:后缀数组,对给定字符串做后缀数组,然后根据height数组确定每个位置做为起点的子串有多少,然后二分查找确定起点位置,但是因为子串的重复的要输出下表小的,所以确定起点后还要确定字典序最小的下标. #include <cstdio> #include <cstring> #includ

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的

HDU - 5008 Boring String Problem (后缀数组+二分+RMQ)

Problem Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest. A substring si...j of the stri

HDU 5008 Boring String Problem

题意:给定一个串长度<=1e5,将其所有的不同的字串按照字典序排序,然后q个询问,每次询问字典序第k小的的起始坐标,并且起始坐标尽量小. 分析: 一开始看错题意,没有意识到是求不同的字串中第k小的,果断不知道怎么做,感觉如果题目改成这样,似乎还有点难度,至少对我来说. 好了,这个题目是考虑不同的字串,首先后缀数组处理,也就是讲后缀按照字典序排序,对于每个后缀开始的字串,如h[i],容易知道i和i-1的后缀的LCP长度为h[i]那么i中除开前h[i]个字串,之后的字串在i-1之前都是没有出现过的,

HDU 5008 Boring String Problem(后缀数组+二分)

题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> #include <map> using namespace std; #define N 501000 #define LL __in

[后缀数组+二分+rmq] hdu 5008 Boring String Problem

有点小可惜这道题,当时整个思路都想到了,就是最后找最左下标的时候不会处理, 然后结束完发现直接暴力就可以了,想到了可是不敢写,10w个a直接就T了啊... 数据太弱了,敢写就过系列啊 T T. 然后希望有大神提供完美思路! 题意: 给一个字符串 然后n次询问 对于每一次询问给一个v 然后问第 l⊕r⊕v+1小的子串的区间   (⊕代表异或) 然后输出l r 这里的l r 就是上一次输出的l r  初始化是0 0 不存在输出0 0  如果多个 输出出现最早的. 思路: 首先后缀数组就不说了,做完之

HDU 5009 Paint Pearls(西安网络赛C题) dp+离散化+优化

转自:http://blog.csdn.net/accelerator_/article/details/39271751 吐血ac... 11668627 2014-09-16 22:15:24 Accepted 5009 1265MS 1980K 2290 B G++ czy   Paint Pearls Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Subm

HDU 5014 Number Sequence(西安网络赛H题)

HDU 5014 Number Sequence 题目链接 思路:对于0-n,尽量不让二进制中的1互相消掉就是最优的,那么只要两个数只要互补就可以了,这样每次从最大的数字,可以找到和他互补的数字,然后这个区间就能确定了,然后剩下的递归下去为一个子问题去解决 代码: #include <cstdio> #include <cstring> const int N = 100005; int n, a[N], ans[N]; int cnt[N]; int count(int x) {

HDU 5015 233 Matrix(西安网络赛I题)

HDU 5015 233 Matrix 题目链接 思路:矩阵快速幂,观察没一列,第一个和为左边加最上面,第二个可以拆为左边2个加最上面,第三个可以拆为为左边3个加最上面,这样其实只要把每一列和每一列右边那列的233构造出一个矩阵,进行矩阵快速幂即可 代码: #include <cstdio> #include <cstring> typedef long long ll; const int N = 15; const int MOD = 10000007; int n, m; s