f[x]=g[x]+d

CF342D. Prefixes and Suffixes

由于关系到前缀和后缀,考虑建出 kmp。

建出 kmp 后,求出 \(S\) 的哪些前缀是 \(S\) 的后缀。那么问题就变为:求一个前缀在 \(S\) 的出现次数。

然后,考虑直接建出 \(fail\) 树后,可以发现 \([1,u]\) 的出现次数即为 \(u\) 的子树个数。

但事实上,由于 \(fail_i<i\),所以不用显示建出树,直接倒序枚举 \(n\)\(1\),然后每次 \(s_{fail_i}\) 加上 \(s_i\) 即可。

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

const int N = 1e5 + 10;

struct node {
	int to, nxt;
} edge[N << 1];
int cnt, head[N];

char ch[N];
int n, pi[N];
int f[N], siz[N];
int ans, l[N], c[N];

void add (int u, int v) {
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].nxt = head[u];
	head[u] = cnt;
}

void solve () {
	n = strlen (ch + 1);
	for (int i = 2, j = 0; i <= n; ++i) {
		while (j != 0 && ch[j + 1] != ch[i]) j = pi[j];
		if (ch[j + 1] == ch[i]) ++j;
		pi[i] = j; add (i, j); add (j, i);
	}
}

void dfs (int u, int fa) {
	siz[u] = 1; f[u] = fa; 
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == fa) continue;
		dfs (v, u); siz[u] += siz[v];
	}
}

int main() {
	scanf ("%s", ch + 1);
	solve();
	for (int i = 1; i <= n; ++i) {
		if (!f[i]) dfs (i, 0);
	}
	int s = n;
	while (s != 0) {
		l[++ans] = s; c[ans] = siz[s];
		s = f[s];
	}
	printf ("%d\n", ans);
	for (int i = ans; i >= 1; --i) printf ("%d %d\n", l[i], c[i]);
	return 0;
}

KrK

不考虑 kmp,考虑 Z 函数。

可以发现,当 \(z_i+i-1=n\) 的时候,代表 \([i,n]\) 是后缀也是前缀,即 \([1,n-i+1]\) 是满足条件的。还是考虑一个前缀在原串出现的次数。

对于一个 \(i\),会对前缀 \([1,j]\) 产生一点贡献,此时 \(1 \le j \le z_i\)。所以直接后缀和优化即可。

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

typedef pair <int, int> ii;

const int Maxn = 100005;

char str[Maxn];
int slen;
int cnt[Maxn];
bool was[Maxn];
int Z[Maxn];
vector <ii> res;

int main()
{
	scanf("%s", str); slen = strlen(str);
	cnt[slen]++; was[slen] = true;
	int L = -1, R = -1;
	for (int i = 1; i < slen; i++) {
		if (i <= R) Z[i] = min(R - i + 1, Z[i - L]);
		while (i + Z[i] < slen && str[Z[i]] == str[i + Z[i]]) Z[i]++;
		if (i + Z[i] - 1 > R) { L = i; R = i + Z[i] - 1; }
		if (i + Z[i] == slen) was[Z[i]] = true;
		cnt[Z[i]]++;
	}
	for (int i = slen - 1; i >= 0; i--)
		cnt[i] += cnt[i + 1];
	for (int i = 1; i <= slen; i++) if (was[i])
		res.push_back(ii(i, cnt[i]));
	printf("%d\n", res.size());
	for (int i = 0; i < res.size(); i++)
		printf("%d %d\n", res[i].first, res[i].second);
	return 0;
}
posted @ 2024-06-08 14:31  wangzhongyuan  阅读(10)  评论(0)    收藏  举报