首页 > 其他分享 >【YBT2023寒假Day6 C】子串染色(SAM)(线段树)(启发式合并)

【YBT2023寒假Day6 C】子串染色(SAM)(线段树)(启发式合并)

时间:2023-02-07 20:35:03浏览次数:91  
标签:子串 rt SAM Day6 YBT2023 int now id pl

子串染色

题目链接:YBT2023寒假Day6 C

题目大意

对于一个 s 的子串 t,把它在 s 中所有出现的位置包含的所有下标染黑,黑色连续段的数目是子串 t 的价值。
然后给你一个 k 和一个串 s,求有多少个 s 的子串价值恰好为 k。

思路

那首先你如果要找到所有的子串,SAM 是一个很好的方法。
于是我们考虑在 fail 树上处理这个问题。

不妨先考虑假如我们一个一个枚举子串,要怎么判断是否可行。
那我们考虑先有子串在原串中出现了一些次数,那右边的坐标我们记作 \(x_1,x_2,...,x_m\)(假设排序了)。
那如果这个子串的长度是 \(k\),那对于满足 \(x_i-x_{i-1}\leqslant k\) 的数量我们记作 \(s\),那这个子串的价值就是 \(m-s\)。
(就是缝起来的地方就相当于少了一个)

那在 SAM 上,一个点代表了一类子串,而且长度是一个区间 \([l,r]\),于是考虑加速或者维护上面的过程。
会发现只跟 \(x_i-x_{i-1}\) 这种东西有关,那我们考虑用一个线段树维护 \(x_{i}-x_{i-1}\) 每个值的出现次数。
那我们找到第 \(m-k\) 小的数 \(a\) 和 \(m-k+1\) 小的数 \(b\),那 \([a,b-1]\) 就是约束。
那配上我们有的区间,就是 \([l,r]\cap [a,b-1]\)

那考虑维护 \(x_i-x_{i-1}\),那因为这个出现的集合 \(x_i\) 是 fail 树上的子树,那我们可以用 dsu on tree,也可以用启发式合并,用 set 维护 \(x_i\),用权值线段树维护 \(x_i-x_{i-1}\),至于查询 \(k\) 大可以直接二分(虽然也可以在线段树上二分就是了)

代码

#include<set>
#include<cstdio>
#include<vector>
#include<cstring>
#define ll long long

using namespace std;

const int N = 1e5 + 100;
char SS[N];
int n, k, dy[N << 1];
ll ans;
struct SAM {
	int tot, lst;
	struct node {
		int len, fa, son[26];
	}d[N << 1];
	
	void Init() {
		tot = lst = 1;
	}
	
	void insert(int x, int id) {
		int p = lst, np = ++tot; lst = np;
		d[np].len = d[p].len + 1; dy[np] = id;
		for (; p && !d[p].son[x]; p = d[p].fa) d[p].son[x] = np;
		if (!p) d[np].fa = 1;
			else  {
				int q = d[p].son[x];
				if (d[q].len == d[p].len + 1) d[np].fa = q;
					else {
						int nq = ++tot; d[nq] = d[q];
						d[nq].len = d[p].len + 1;
						d[q].fa = d[np].fa = nq;
						for (; p && d[p].son[x] == q; p = d[p].fa) d[p].son[x] = nq;
					}
			}
	}
}S;

struct XD_tree {
	int f[N << 6], ls[N << 6], rs[N << 6], tot;
	
	void update(int &now, int l, int r, int pl, int x) {
		if (!now) now = ++tot;
		f[now] += x;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		if (pl <= mid) update(ls[now], l, mid, pl, x);
			else update(rs[now], mid + 1, r, pl, x);
	}
	
	int query(int now, int l, int r, int L, int R) {
		if (!now) return 0;
		if (L > R) return 0;
		if (L <= l && r <= R) return f[now];
		int mid = (l + r) >> 1, re = 0;
		if (L <= mid) re += query(ls[now], l, mid, L, R);
		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
		return re;
	}
}T;

vector <int> G[N << 1];
set <int> s[N << 1];
int id[N << 1], rt[N << 1];

void merge(int &x, int &y) {
	if (s[x].size() < s[y].size()) swap(x, y);
	for (set <int> ::iterator it = s[y].begin(); it != s[y].end(); it++) {
		int now = *it;
		set <int> ::iterator pl = s[x].lower_bound(now);
		int r = (pl == s[x].end()) ? 0 : *pl;
		int l = (pl == s[x].begin()) ? 0 : *(--pl);
		if (l && r) T.update(rt[x], 1, n, r - l, -1);
		if (l) T.update(rt[x], 1, n, now - l, 1);
		if (r) T.update(rt[x], 1, n, r - now, 1);
		s[x].insert(now);
	}
}

void dfs(int now) {
	if (dy[now]) s[id[now]].insert(S.d[now].len);
	for (int i = 0; i < G[now].size(); i++) {
		dfs(G[now][i]);
		merge(id[now], id[G[now][i]]);
	}
	
	if (now == 1) return ;
	int m = s[id[now]].size(); 
	int L = S.d[S.d[now].fa].len + 1, R = S.d[now].len, rel = L;
	while (L <= R) {
		int mid = (L + R) >> 1;
		if (T.query(rt[id[now]], 1, n, 1, mid) >= m - k) rel = mid, R = mid - 1;
			else L = mid + 1;
	}
	if (T.query(rt[id[now]], 1, n, 1, rel) != m - k) return ;
	L = S.d[S.d[now].fa].len + 1; R = S.d[now].len; int rer = L;
	while (L <= R) {
		int mid = (L + R) >> 1;
		if (T.query(rt[id[now]], 1, n, 1, mid) <= m - k) rer = mid, L = mid + 1;
			else R = mid - 1;
	}
	ans += rer - rel + 1;
}

int main() {
	freopen("gnirts.in", "r", stdin);
	freopen("gnirts.out", "w", stdout);
	
	scanf("%s", SS + 1); n = strlen(SS + 1);
	scanf("%d", &k);
	
	S.Init();
	for (int i = 1; i <= n; i++) S.insert(SS[i] - 'a', i);
	for (int i = 2; i <= S.tot; i++) G[S.d[i].fa].push_back(i); 
	
	for (int i = 1; i <= S.tot; i++) id[i] = i;
	dfs(1);
	printf("%lld", ans);
	
	return 0;
}

标签:子串,rt,SAM,Day6,YBT2023,int,now,id,pl
From: https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day6_C.html

相关文章

  • 【YBT2023寒假Day6 A】寄蒜挤河(计算几何)(线段树)
    寄蒜挤河题目链接:YBT2023寒假Day6A题目大意有一些二维平面上的点,按顺序加入,每次加入一个点后,问你图上有多少个三元点对使得它们构成的三角形严格包含原点,就是原点不能......
  • [LeetCode] 1604. Alert Using Same Key-Card Three or More Times in a One Hour Per
    LeetCodecompanyworkersusekey-cardstounlockofficedoors.Eachtimeaworkerusestheirkey-card,thesecuritysystemsavestheworker'snameandthetime......
  • SA 与 SAM
    SA具体一些,在有些信息的处理上显得无力;SAM较为抽象,但是方便维护很多信息。期望受众是会打两个板子不会应用者。虽然我会对板子的理解做一些碎碎念。基本操作\(\rmSA......
  • day67-sql中的事务
    事务要么都成功要么都失败事务原则:ACID原子性,一致性,隔离性,持久性原子性:要么一起成功要么一起失败一致性:针对一个事务操作前后状态一致持久性:事务结束后的数据不会随......
  • 20天零基础自学Python | Day6 运算符大全
    大家好,我是宁一。运算符是编程语言中最基本的知识点,是必须要掌握的,不仅适用于Python,其他编程语言也都能用到。1、算术运算符(1)加减乘除跟我们上学时学的都是一样的,注意乘法和......
  • 【YBT2023寒假Day5 C】路径计数(数学)(生成函数)
    路径计数题目链接:YBT2023寒假Day5C题目大意有一个h*w的网格,你要从左上角走到右下角,每次可以向右或者向下,定义一条路径的分数是它经过的位置的权值和。每次会选择权......
  • 【YBT2023寒假Day5 B】全面沦陷(tarjan)
    全面沦陷题目链接:YBT2023寒假Day5B题目大意给你一个有向图,问你有多少个点可以到达所有点。一个点x能到达一个点y当且仅当在原图有路径或在把边反向的图中有路径。......
  • day66 -收集表单数据
    收集表单数据<body><!--收集表单数据:若<inputtype="text"则v-model收集的是value值,用户输入的就是value若<inputtype="radio"则v-model收集的......
  • 【YBT2023寒假Day4 C】樱桃莓莓(交互)(四毛子分块)(线段树)
    樱桃莓莓题目链接:YBT2023寒假Day4C题目大意有一个黑盒操作满足交换律和结合律,有n个数,q次询问,每次选m个下标,你要计算所有不包含那m个下标的数进行黑盒操作之后的......
  • 【YBT2023寒假Day4 B】人人人数(数学)
    人人人数题目链接:YBT2023寒假Day4B题目大意随机生成一个长度为m且每个元素都是1~n的整数单调不降序列,问你这个序列的众数的期望出现次数是多少。(随机是所有满足条......