洛谷 P3435 [POI2006] OKR-Periods of Words(扩展KMP+线段树做法)

题目链接

https://www.luogu.com.cn/problem/P3435

思路

我们先用扩展KMP算法对字符串 s s s进行预处理,求出 z z z数组。

对于字符串 s s s的第 i i i个字符,对于其 z z z数组的值 z i zi zi,如果 z i ≥ 0 zi \ge 0 zi≥0,则区间 i , i + z \[ i − 1 ] i,i+z\[i-1] i,i+z\[i−1]上的前缀最大值将变成 i i i(我们假设字符串的下标从 0 0 0开始)。

因此我们可以在统计答案的过程中,用线段树来维护区间最大值,保证计算的答案是最优的。

时间复杂度: O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n)

代码

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

// #define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 1e6 + 5, M = 2e2 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

std::mt19937 rnd(time(0));

int n;
string s;
// z[i]表示 s 和 s[i:] 匹配的最大前缀长度
vector<int> zfunc(const string& s) {
	int n = s.size();
	vector<int> z(n);
	for (int i = 1, l = 0, r = 0; i < n; i++) {
		if (i <= r) z[i] = min(z[i - l], r - i + 1);
		while (i + z[i] < n && s[i + z[i]] == s[z[i]]) z[i]++;
		if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
	}
	z[0] = n;
	return z;
}
struct segmenttree
{
	struct node
	{
		int l, r, maxx, tag;
	};
	vector<node>tree;

	segmenttree(): tree(1) {}
	segmenttree(int n): tree(n * 4 + 1) {}

	void pushup(int u)
	{
		auto &root = tree[u], &left = tree[u << 1], &right = tree[u << 1 | 1];
		root.maxx = max(left.maxx, right.maxx);
	}

	void pushdown(int u)
	{
		auto &root = tree[u], &left = tree[u << 1], &right = tree[u << 1 | 1];
		if (root.tag != 0)
		{
			left.tag = root.tag;
			right.tag = root.tag;
			left.maxx = root.tag;
			right.maxx = root.tag;
			root.tag = 0;
		}
	}

	void build(int u, int l, int r)
	{
		auto &root = tree[u];
		root = {l, r};
		if (l == r)
		{
			root.maxx = 0;
		}
		else
		{
			int mid = l + r >> 1;
			build(u << 1, l, mid);
			build(u << 1 | 1, mid + 1, r);
			pushup(u);
		}
	}

	void modify(int u, int l, int r, int val)
	{
		auto &root = tree[u];
		if (root.l >= l && root.r <= r)
		{
			root.maxx = val;
			root.tag = val;
			return;
		}
		pushdown(u);
		int mid = root.l + root.r >> 1;
		if (l <= mid) modify(u << 1, l, r, val);
		if (r > mid) modify(u << 1 | 1, l, r, val);
		pushup(u);
	}

	int query(int u, int l, int r)
	{
		auto &root = tree[u];
		if (root.l >= l && root.r <= r)
		{
			return root.maxx;
		}
		pushdown(u);
		int mid = root.l + root.r >> 1;
		int res = -inf;
		if (l <= mid) res = query(u << 1, l, r);
		if (r > mid) res = max(res, query(u << 1 | 1, l, r));
		return res;
	}
};
void solve(int test_case)
{
	cin >> n >> s;
	vector<int>z = zfunc(s);
	segmenttree smt(n);
	smt.build(1, 1, n);
	i64 ans = 0;
	for (int i = 0; i < n; i++)
	{
		int low = i + 1, high = min(i + z[i], n);
		if (low <= high)
		{
			smt.modify(1, low, high, i);
		}
		int maxx = smt.query(1, i + 1, i + 1);
		ans += maxx;
	}
	cout << ans << endl;
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int test = 1;
	// cin >> test;
	for (int i = 1; i <= test; i++)
	{
		solve(i);
	}
	return 0;
}
相关推荐
励志不掉头发的内向程序员5 小时前
【从零写一个CAD 04】中键拖动平移:抓住一个点,让它一直待在鼠标底下
开发语言·c++·qt·学习·系统架构·计算机外设
UIU1145 小时前
同一个变量在两个 .c 文件里类型不一致,程序会怎样?
c++·学习·c#
Logic1017 小时前
C语言/数据结构字符串题解:按字符类型重排(数字→字母→符号)——三趟扫描稳定分区
c语言·数据结构·字符串·时间复杂度·计数排序·算法题·ascii
weixin_4241836113 小时前
开放—封闭原则实战(下):基于策略的流程编排与编译期多态
c++·分布式·系统架构
Logic10113 小时前
C语言/数据结构位运算题解:异或XOR找出学习计划中的“独特学习时间“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
wuminyu14 小时前
C++协程实现接收端的零拷贝Buffer管理原理剖析
java·linux·c语言·jvm·c++
小蒋学算法14 小时前
算法-交替删除操作后最后剩下的整数-跟着灵茶山艾府学算法
数据结构·算法
m0_3807438714 小时前
C++中的freopen的用法实例详解
开发语言·c++
荆棘鸟智能14 小时前
林业遥感长势评估怎么做?从NDVI时序分析到LiDAR蓄积量回归
人工智能·算法·智慧林业
jimy115 小时前
polymorph.cpp里面的“析构函数、智能指针”问题
开发语言·c++