【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. 父组件调用子组件 viewChild

    父组件调用子组件 1.在子组件的ts中声明一个变量 public  lineout:any="你好,我是被父组件调用的子组件";  2.在父组件的html中写入 (引入子组件) & ...

  2. 解决百度UEditor列出图片列出文件时返回的文件路径为磁盘绝对路径的问题

    替换绝对路径为空 String str = new ActionEnter(request, buffer.toString(), "config.json").exec(); S ...

  3. 小记:iterator && auto

    小记:iterator && auto iterator 众所周知,我们有一种强大的东西,它叫做STL,比如queue.vector.set.map.multimap .deque等. ...

  4. Git拉取远程分支命令

    如果我们想从Git仓库中拉取一个分支到本地,此处假如远程分支为develop,本地要创建的分支为dev,可以使用以下命令: git init    //初始化本地Git仓库 git remote ad ...

  5. Mysql序列(七)—— order by优化

    前言 在mysql中满足order by的处理方式有两种: 让索引满足排序,即扫描有序索引然后再找到对应的行结果,这样结果即是有序: 使用索引查询出结果或者扫描表得到结果然后使用filesort排序: ...

  6. linux 通过tar直接打包方式 迁移oracle的软件包环境:rdbms/lib/config.c

    step1.修改sysctl.conf step2.修改ulimit.conf step3.打包源oracle安装包 step4.通过id{oracle的安装运行用户} 获取安装oracle的[ORA ...

  7. Blend 硬货 绑定

    原文:Blend 硬货 绑定 开始讲一点 硬技能 怎么用Blend实现绑定 效果 详细说一下绑定 1)default 2)OneTime 3) One Way 4)TwoWay 5) OneWayto ...

  8. digital clock based C

    /********************************** * Name : timeDisplay.cpp * Purpose: Display digital clock accord ...

  9. em与rem之间的区别以及移动设备中的rem适配方案

    em与rem之间的区别: 共同点: 它们都是像素单位 它们都是相对单位 不同点: em大小是基于父元素的字体大小 rem大小是基于根元素(html)的字体的大小 实例: <!DOCTYPE ht ...

  10. 通过SOFA看Java服务端如何实现运行时的模块化

    本文阅读时间大约7分钟. 今天我们谈谈SOFA模块化,首先看一段SOFA的介绍: SOFABoot是蚂蚁金服开源的基于Spring Boot的研发框架,它在Spring Boot的基础上,提供了诸如 ...