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;
}