NSUBSTR - Substrings

SAM 板题。

考虑对于一个点 uu,其状态对应所有字符串的长度都在 [lenfau+1,lenu][len_{fa_u}+1, len_u] 间,且连续。

容易在后缀链接树上 DP 求出子串出现次数。于是我们用线段树做区间取 max\max,单点查询即可。复杂度 O(nlogn)O(n \log n)

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

const int N = 5e5 + 5;

struct Node
{
	int son[26], fa, len;
}g[N];

int last = 1, tot = 1;
int f[N];
string s;

void extend(int c)
{
	int p = last, np = last = ++tot;
	g[np].len = g[p].len + 1, f[np] = 1;
	for (; p && g[p].son[c] == 0; p = g[p].fa) g[p].son[c] = np;
	if (!p) g[np].fa = 1;
	else
	{
		int q = g[p].son[c];
		if (g[q].len == g[p].len + 1) g[np].fa = q;
		else
		{
			int nq = ++tot;
			g[nq] = g[q];
			g[nq].len = g[p].len + 1;
			g[np].fa = g[q].fa = nq;
			for (; p && g[p].son[c] == q; p = g[p].fa) g[p].son[c] = nq;
		}
	}
}

vector<int> G[N];

class SegmentTree
{
public:
	struct Node
	{
		int l, r, maxn;
		int tag;
	}tr[N << 2];
	void pushup(int u)
	{
		tr[u].maxn = max(tr[u << 1].maxn, tr[u << 1 | 1].maxn);
	}
	void pushdown(int u)
	{
		if (tr[u].tag)
		{
			tr[u << 1].maxn = max(tr[u << 1].maxn, tr[u].tag);
			tr[u << 1].tag = max(tr[u << 1].tag, tr[u].tag);
			tr[u << 1 | 1].maxn = max(tr[u << 1 | 1].maxn, tr[u].tag);
			tr[u << 1 | 1].tag = max(tr[u << 1 | 1].tag, tr[u].tag);
			tr[u].tag = 0;
		}
	}
	void build(int u, int l, int r)
	{
		tr[u] = { l, r, 0, 0 };
		if (l == r) return;
		int mid = l + r >> 1;
		build(u << 1, l, mid);
		build(u << 1 | 1, mid + 1, r);
	}
	void update(int u, int l, int r, int maxn)
	{
		if (tr[u].l >= l and tr[u].r <= r)
		{
			tr[u].maxn = max(tr[u].maxn, maxn);
			tr[u].tag = max(tr[u].tag, maxn);
			return;
		}
		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) update(u << 1, l, r, maxn);
		if (r > mid) update(u << 1 | 1, l, r, maxn);
		pushup(u);
	}
	int query(int u, int x)
	{
		if (tr[u].l == tr[u].r)
		{
			return tr[u].maxn;
		}
		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (x <= mid) return query(u << 1, x);
		return query(u << 1 | 1, x);
	}
}sgt;

void dfs(int u)
{
	for (auto& j : G[u])
	{
		dfs(j);
		f[u] += f[j];
	}
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr);
	cin >> s;
	for (auto& i : s) extend(i - 'a');
	for (int i = 2; i <= tot; i++) G[g[i].fa].emplace_back(i);
	sgt.build(1, 1, tot);
	dfs(1);
	for (int i = 2; i <= tot; i++)
	{
		sgt.update(1, g[g[i].fa].len + 1, g[i].len, f[i]);
	}
	for (int i = 1; i <= s.size(); i++)
	{
		cout << sgt.query(1, i) << "\n";
	}
	return 0;
}
posted @   HappyBobb  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示