【border相关】【P3426】 [POI2005]SZA-Template

Description

给定一个字符串 \(S\),要求一个最短的字符串 \(T\),使得 \(S\) 可以由 \(T\) 不断在后面接上自身得到。在拼接的时候, \(T\) 的某个后缀如果与某个前缀相同,则相同的部分可以算作一个,不再重复出现。

Limitations

\(1 \leq |S| \leq 5 \times 10^5\)

Solution

介绍一个叫 \(border\) 树的东西,在 OI 中被称作 \(next\) 树。

记 \(S\) 的前缀 \(i\) 的最长 \(border\) 为 \(border_i\),考虑在 \(i\) 和 \(border_i\) 之间连一条边,最终会形成一棵以 \(0\) 为根的树。

证明上,考虑这棵树有 \(n + 1\) 个节点,而显然 \(border_i < i\),因此每个节点连向 \(border\) 的边都是互不重复的,共有 \(n\) 条边,由此可以证明这是一颗树。

这棵树有两个优美的性质:

节点 \(u\) 的祖先集合是 \(u\) 的所有 \(border\) 集合

节点 \(u\) 的后代集合是 \(u\) 能作为 \(border\) 的 \(S\) 的前缀子串集合

对于性质 \(1\),根据定义,\(u\) 的父节点是 \(u\) 的最长 \(border\),迭代证明即可。

性质 \(2\) 可以由性质 \(1\) 反推得到。

现在考虑本题。

一个显而易见的结论是 \(T\) 一定是 \(S\) 的 \(border\)。

因此我们考虑枚举 \(S\) 的所有 \(border\),我们发现对于长度为 \(i\) 的 \(border\),如果将他在 \(border\) 树上的后代拿下来排序以后相邻两数差值的最大值大于 \(i\),则这个 \(border\) 不能作为答案,因为对于插值最大的两个数,在拼接到左边的位置以后再加一个长度为 \(i\) 的 \(T\) 不能拼接到右侧的数,反之可以证明这个 \(border\) 是合法的。

我们考虑维护 \(border\) 的所有后代,从长到短枚举 \(border\) 时,相当于从 \(border\) 树的某个叶节点一直枚举到根,我们发现 \(border\) 变短时只会加入一些节点而不会删除,因此用一个 set 去维护这些后代,用 multiset 维护插值最大值即可。

时间复杂度 \(O(|S| \log |S|)\)

Code

写代码的时候发现一个有关 multiset 的有趣的事:erase某个值的时候,会将全部的该值删掉,如果想要只删掉一个,需要 s.erase(s.find(x))。

#include <cstdio>
#include <set>
#include <vector>
#include <algorithm> const int maxn = 500005; int n, ans;
char MU[maxn];
int border[maxn];
std::set<int>s;
std::multiset<int>ms;
std::vector<int>son[maxn]; void KMP();
int ReadStr(char *p);
void dfs(const int u);
void update(const int x); void KMP(); int main() {
freopen("1.in", "r", stdin);
n = ReadStr(MU + 1);
KMP();
update(n);
for (int i = border[n], j = n; i; j = i, i = border[i]) {
update(i);
for (auto u : son[i]) if (u != j) {
dfs(u);
}
if (*(--ms.end()) <= i) {
ans = i;
}
}
qw(ans, '\n', true);
return 0;
} int ReadStr(char *p) {
auto beg = p;
do *p = IPT::GetChar(); while ((*p > 'z') || (*p < 'a'));
do *(++p) = IPT::GetChar(); while ((*p >= 'a') && (*p <= 'z'));
*p = 0;
return p - beg;
} void KMP() {
for (int i = 2, j = 0; i <= n; ++i) {
while (j && (MU[i] != MU[j + 1])) j = border[j];
if (MU[i] == MU[j + 1]) ++j;
son[border[i] = j].push_back(i);
}
} void dfs(const int u) {
update(u);
for (auto v : son[u]) {
dfs(v);
}
} void update(const int x) {
auto u = s.insert(x).first, ftmp = u, btmp = u;
--ftmp; ++btmp;
if ((u != s.begin()) && (btmp != s.end())) {
ms.erase(ms.find(*btmp - *ftmp));
}
if (u != s.begin()) {
ms.insert(x - *ftmp);
}
if (btmp != s.end()) {
ms.insert(*btmp - x);
}
}

【border相关】【P3426】 [POI2005]SZA-Template的更多相关文章

  1. P3426 [POI2005]SZA-Template

    P3426 [POI2005]SZA-Template 链接 分析: 首先T一定是S的一个前缀,也是一个后缀. 判断一个前缀s[1...i]是不是满足条件,那么求出s[1...i]在s中出现的所有位置 ...

  2. 2021.11.09 P3426 [POI2005]SZA-Template(KMP+DP)

    2021.11.09 P3426 [POI2005]SZA-Template(KMP+DP) https://www.luogu.com.cn/problem/P3426 题意: 你打算在纸上印一串字 ...

  3. 用CSS border相关属性画三角形

    效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva2l3aV9jb2Rlcg==/font/5a6L5L2T/fontsize/400/fill/I0J ...

  4. Border性质习题与证明

    KMP 第一次接触 \(border\) 都是先从 KMP 开始的吧. 思想在于先对于一个串自匹配以求出 fail 指针(也就是 border) 然后就可以在匹配其他串的时候非常自然的失配转移.在此顺 ...

  5. template package (godoc 翻译)

    template 包 概述(Overview) template 包实现了数据驱动模板用于生成文本输出. 要生成HTML输出,请参阅html/template包,它具有与此包相同的接口,但会自动保护H ...

  6. Element-UI使用相关问题

    1.如何修改el-dialog的样式? 要修改dialog的样式不能直接在<style scoped>中修改,这样修改后不会生效.做法是把scoped去掉,然后在dialog标签上自定义一 ...

  7. Django-mtv开发模式

    从著名的MVC模式开始说起 所谓的MVC就是把Web应用分为模型(M)控制器(C)和视图(V)三层,他们之间以一种插件式的.松耦合的房还是 连接在一起,模型负责业务对象与数据库的映射(ORM),视图负 ...

  8. UWP Button添加圆角阴影(一)

    原文:UWP Button添加圆角阴影(一) 众所周知,17763之前的UWP控件,大部分是没有圆角属性的:而阴影也只有17763中的ThemeShadow可以直接在xaml中使用,之前的版本只能用D ...

  9. Silverlight 之 浅析

    一.silverlight定义及作用 silverlight用XAML来做前端界面,用.NET或者JS作为程序脚本支持,在浏览器内外运行的应用.可以认为和FLASH 和ADOBE AIR有很大的功能重 ...

随机推荐

  1. Android添加新按键

    1.前言 在Android开发中可能会遇到添加新的按键的需求,本文将简单介绍如何在Android系统中完成一个新的按键的添加. 当系统有新的按键需要添加时,Linux内核下的键码到Android系统中 ...

  2. 【Oracle】重做undo表空间

    重做undo表空间 场景: alert日志,报了如下错误: [oraprod@arpinfo bdump]$ tail -f alert_PROD.log Errors in file /ora115 ...

  3. Linux 分区管理器

    下面的列表没有特定的排名顺序.大多数分区工具应该存在于 Linux 发行版的仓库中. GParted 这可能是 Linux 发行版中最流行的基于 GUI 的分区管理器.你可能已在某些发行版中预装它.如 ...

  4. MD5加密方法HashPasswordForStoringInConfigFile(string,string)过时问题处理方法

    最近写代码的时候一直有一种很奇怪的感觉,查了一下发现原来是系统中的MD5加密方法报了过时的警告,虽然对系统没有任何影响,但是开发过程中绿色波浪线就像挥之不去的阴影一样.开发人员多多少少都会有点强迫症, ...

  5. StackExchange.Redis 封装

    博主最近开始玩Redis啊~~ 看了很多Redis的文章,感觉有点云里雾里的,之前看到是ServiceStack.Redis,看了一些大佬封装的Helper类,还是懵懵的QAQ 没办法啊只能硬着**上 ...

  6. mvc_第二遍

    之前,我们学习了mvc最基本的架构和用法. 在学习了真正的属性.委托和列表之后,我们就可以使用微软提倡的模型了. 需要注意的是,这种模型以及它的工作方式,身上有着浓浓的微软印记.在方便的同时,也要自行 ...

  7. 来看一下Java中“-”与equeals的区别

    简介: == ==是比较两个变量的值,如果是基本数据类型,那么就是比较的基本数据的大小值 情况一 int a=1; int b=1; System.out.println(a==b); 以上图中:== ...

  8. 使用Vue CLI构建Vue项目

    第一步:首先在控制台输入vue --version,如果出现版本号则进入第三步:否则进入第二步: 第二步:输入npm install cnpm -g --registry=https://regist ...

  9. 操作mysql数据库的一些命名

    一.打开数据库 1.1.打开命令行输入:mysql -u root -p 1.2.root是本地数据库的用户名,然后输入数据库的密码进入. 二.数据库操作 2.1.创建一个数据库:create dat ...

  10. vue+element省市县的二级联动功能

    项目中有选择省市县的需求,先选择省,再选择县 解决这个需求也不是很难,总体思路就是看后端接口, 一般后端接口都是请求参数为 0 返回省的数据,不为 0 的话返回相对应的市的数据 template代码: ...