ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【YBT2022寒假Day1 C】相似子串(SA)(RMQ)(LCP)

2022-02-06 08:31:31  阅读:191  来源: 互联网

标签:RMQ return LCP int YBT2022 mid re 100001 now


相似子串

题目链接:YBT2022寒假Day1 C

题目大意

给你一个数字串,然后每次问你一个子串,问这个数字串中有多少个子串(包括自己)跟它相似。
定义相似是长度相同,且数组可以通过把数字集体置换成另一种形式得到另一个数组。

思路

考虑把这个相似的属性转化一下,把相等的颜色要求去掉:
可以从原数字构造新数字串 \(s\),构造方法为如果这个数字第一次出现就是 \(0\),否则就是它跟上个这个数字距离的位置。

那我们的问题就变成了看询问串的 \(s\) 串跟多少个后缀的 \(s\) 串 LCP 大于等于询问串的大小。
但是你会发现有点问题,就是你截取一个子串的话直接用原串的子串会有一些地方变成 \(0\),但是由于只有 \(0\sim 9\) 所以至多变 \(10\) 个地方。
所以我们考虑暴力把这些段找到分开来,一个一个处理。

那思路大概就有了,首先把原串搞后缀排序,求出 height 数组,然后把每个后缀试着分割记录下来。
然后由于我们最后也要 LCP,所以我们也要给所有分割好的后缀串再排一次序。(大概就是一个区间一个区间的比较,相同就看下一个,不同就比较那里面的)
然后我们就可以用最后排序的 height 数组二分左右的量求出答案了。

emmm具体细节很多可以看看代码来理解。

代码

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int n, q, l, r, s[100001], nxt[100001][11];
int a[100001], lst, ban[11], pl[100001], abab[100001];

int sa[100001], xx[100001], yy[100001], fir[100001], log2_[100001];
int height[100001][21], rnk[100001], tong[100001], kind, ynum;

void Sort(int m, int *x, int *y) {
	for (int i = 1; i <= m; i++) tong[i] = 0;
	for (int i = 1; i <= n; i++) tong[x[i]]++;
	for (int i = 2; i <= m; i++) tong[i] += tong[i - 1];
	for (int i = n; i >= 1; i--) sa[tong[fir[i]]--] = y[i];
}

void SA(int *r, int *sa, int n, int m) {
	int *x = xx, *y = yy;
	for (int i = 1; i <= n; i++) x[i] = r[i] + 1;
	for (int i = 1; i <= n; i++) y[i] = i;
	for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
	Sort(m, x, y);
	
	for (int j = 1; j < n; j <<= 1) {
		ynum = 0;
		for (int i = n - j + 1; i <= n; i++)
			y[++ynum] = i;
		for (int i = 1; i <= n; i++)
			if (sa[i] > j) y[++ynum] = sa[i] - j;
		for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
		Sort(m, x, y);
		
		swap(x, y);
		kind = 1;
		x[sa[1]] = 1;
		for (int i = 2; i <= n; i++)
			if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = kind;
				else x[sa[i]] = ++kind;
		
		if (kind == n) break;
		m = kind;
	}
}

void get_height(int *r, int *sa, int n) {
	int k = 0, j;
	for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
	for (int i = 1; i <= n; i++) {
		if (k) k--;
		j = sa[rnk[i] - 1];
		while (r[i + k] == r[j + k] && i + k <= n && j + k <= n) k++;
		height[rnk[i]][0] = k;
	}
}

void get_RMQ(int n) {
	log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1;
	for (int i = 1; i <= 20; i++)
		for (int j = 1; j + (1 << i) - 1 <= n; j++)
			height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]);
}

int RMQ1(int l, int r) {
	if (!l || !r) return 0;
	if (l == r) return n - l + 1;
	l = rnk[l]; r = rnk[r]; if (l > r) swap(l, r);
	l++; int k = log2_[r - l + 1];
	return min(height[l][k], height[r - (1 << k) + 1][k]);
}

int RMQ2(int l, int r) {
	l++; int k = log2_[r - l + 1];
	return min(height[l][k], height[r - (1 << k) + 1][k]);
}

struct node {
	int l, r;
};
struct fgd {
	vector <node> a;
	int id;
}nt[100001];

int cp(node x, node y) {
	if (!x.l && !y.l) return 2;
	if (!x.l) return 1; if (!y.l) return 0;
	int len = RMQ1(x.l, y.l);
	if (len >= x.r - x.l + 1 || len >= y.r - y.l + 1) {
		if (x.r - x.l + 1 == y.r - y.l + 1) return 2;
		return x.r - x.l + 1 < y.r - y.l + 1;
	}
	return s[x.l + len] < s[y.l + len];
}

bool cmp(fgd x, fgd y) {
	int now = 0;
	while (1) {
		if (now >= x.a.size()) return 0;
		if (now >= y.a.size()) return 1;
		int op = cp(x.a[now], y.a[now]);
		if (op != 2) return op;
		now++;
	}
}

int LCP(fgd x, fgd y) {
	int re = 0, now = 0;
	while (now < x.a.size() && now < y.a.size() && cp(x.a[now], y.a[now]) == 2)
		re += x.a[now].r - x.a[now].l + 1, now++;
	if (now < x.a.size() && now < y.a.size())
		re += min(RMQ1(x.a[now].l, y.a[now].l), min(x.a[now].r - x.a[now].l + 1, y.a[now].r - y.a[now].l + 1));
	return re;
} 

int main() {
//	freopen("similar.in", "r", stdin);
//	freopen("similar.out", "w", stdout);
	
	scanf("%d %d", &n, &q);
	for (int i = 1; i <= n; i++) {
		scanf("%1d", &a[i]);
		if (!ban[a[i]]) s[i] = 0;
			else s[i] = i - ban[a[i]];
		ban[a[i]] = i;
	}
	
	nxt[n][a[n]] = n;
	for (int i = n - 1; i >= 1; i--) {
		memcpy(nxt[i], nxt[i + 1], sizeof(nxt[i]));
		nxt[i][a[i]] = i;
	}//SA
	SA(s, sa, n, n);
	get_height(s, sa, n);
	get_RMQ(n);
	
	for (int i = 1; i <= n; i++) {//分成若干段
		nt[i].id = i;
		memcpy(ban, nxt[i], sizeof(ban));
		sort(ban, ban + 9 + 1);
		int now = i;
		for (int j = 0; j <= 9; j++) {
			if (!ban[j]) continue;
			if (ban[j] > now) nt[i].a.push_back((node){now, ban[j] - 1});
			nt[i].a.push_back((node){0, 0});
			now = ban[j] + 1;
		}
		if (now <= n) nt[i].a.push_back((node){now, n});
	}
	sort(nt + 1, nt + n + 1, cmp);//排序
	
	for (int i = 1; i <= n; i++)
		pl[nt[i].id] = i;
	for (int i = 2; i <= n; i++)//用前面的 LCP 来搞新的 height 数组
		abab[i] = LCP(nt[i - 1], nt[i]);
	for (int i = 2; i <= n; i++) height[i][0] = abab[i];
	get_RMQ(n);
	
	while (q--) {
		scanf("%d %d", &l, &r); l ^= lst; r ^= lst;
		
		int x = pl[l], sz = r - l + 1, ans = 1;//height两边分别二分
		int L = x + 1, R = n, re = x;
		while (L <= R) {
			int mid = (L + R) >> 1;
			if (RMQ2(x, mid) >= sz) re = mid, L = mid + 1;
				else R = mid - 1;
		}
		ans += re - x;
		
		L = 1; R = x - 1; re = x;
		while (L <= R) {
			int mid = (L + R) >> 1;
			if (RMQ2(mid, x) >= sz) re = mid, R = mid - 1;
				else L = mid + 1;
		}
		ans += x - re;
		
		lst = ans; printf("%d\n", lst);
	}
	
	return 0;
}

标签:RMQ,return,LCP,int,YBT2022,mid,re,100001,now
来源: https://www.cnblogs.com/Sakura-TJH/p/YBT2022Day1_C.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有