[TJOI2019]甲苯先生和大中锋的字符串

Description

给定模式串 s 和一个整数 k ,求所有出现次数为 k 次的字符串中长度出现次数最多的长度。

多测。

\(n \leq 10 ^ 5,\ \ \sum{n} \leq 3 \cdot 10 ^ 6\)

Solution

这些一看起来暴力就很劣的字符串可能多半和 SAM 有点关系。

而且这题个人感觉比较萌萌,主要是他让我们干的事全部写的明明白白。

所以说我们要先能知道那些字符串出现次数为 k ,维护 SAM 上状态的 siz 就可以了,那么对于每个状态如果满足条件,所有包含在这个长度里的字符串都有 1 的贡献,区间加,差分,最后统一统计。

Code

Code

/*

*/
#include 
using namespace std;
typedef long long ll;
const int N = 6e6 + 10;
int k, arr[N];
inline int read() {
	char ch = getchar();
	int s = 0, w = 1;
	while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
	while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
	return s * w;
}
struct SAM {
	int n, cnt, las, len[N], link[N], ch[N][26];
	char s[N]; int tong[N], rk[N], siz[N];
	inline void init() {cnt = las = 1; memset(ch[1], 0, sizeof(ch[1]));}
	inline void SAM_stru(int c) {
		int cur = ++cnt, p = las;
		memset(ch[cur], 0, sizeof(ch[cur]));
		las = cur;
		len[cur] = len[p] + 1; siz[cur] = 1;
		while (p && !ch[p][c]) ch[p][c] = cur, p = link[p];
		if (!p) {link[cur] = 1; return ;}
		int q = ch[p][c];
		if (len[p] + 1 == len[q]) {link[cur] = q; return ;}
		int clo = ++cnt;
		link[clo] = link[q]; len[clo] = len[p] + 1;
		link[q] = link[cur] = clo; siz[clo] = 0;
		memcpy(ch[clo], ch[q], sizeof(ch[clo]));
		while (p && ch[p][c] == q) ch[p][c] = clo, p = link[p];
	}
	inline void Tong_sort() {
		for (int i = 1; i <= cnt; ++i) tong[i] = 0;
		for (int i = 1; i <= cnt; ++i) ++tong[len[i]];
		for (int i = 1; i <= cnt; ++i) tong[i] += tong[i - 1];
		for (int i = 1; i <= cnt; ++i) rk[tong[len[i]]--] = i;
		for (int i = cnt, u, v; i; --i) {
			v = rk[i]; u = link[v];
			siz[u] += siz[v];
			if (siz[v] == k) ++arr[len[u] + 1], --arr[len[v] + 1];
		}
	}
} s;
inline void mian() {
	s.init();
	scanf("%s", s.s + 1); s.n = strlen(s.s + 1); k = read();
	for (int i = 1; i <= s.n; ++i) s.SAM_stru(s.s[i] - 'a');
	s.Tong_sort();
	int mx = -1, ans = -1;
	for (int i = 1; i <= s.n; ++i) {
		arr[i] += arr[i - 1];
		if (mx <= arr[i]) {
			mx = arr[i]; ans = i;
		}
	}
	printf("%d\n", (mx > 0) ? ans : -1);
	for (int i = 1; i <= s.n + 1; ++i) arr[i] = 0;
}
int main() {
	int t = read();
	while (t--) mian();
	return 0;
}

posted @ 2022-03-13 10:12  Illusory_dimes  阅读(29)  评论(0编辑  收藏  举报