[BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)

题面

Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段作为模版. 然后将模版重复喷涂到相应的位置后就得到了他想要的字符序列.一个字符可以被喷涂很多次,但是一个位置不能喷涂不同的字符.做一个模版很费工夫,所以他想要模版的长度尽量小,求最小长度是多少.

拿样例来说ababbababbabababbabababbababbaba , 模版为前8个字符ababbaba, 喷涂的过程为: ababbababbabababbabababbababbaba

分析

分析模板串,我们会发现3条性质

模板串性质:

  1. 一个模板串A是要求的文本串B的公共前后缀
  2. 如果一个模板串A有另一个模板串B(也就是B可以覆盖A),那么B是比A更优的一个解
  3. 如果模板串A可以完全覆盖文本串B,那么A在B中的匹配位置(按照开头算)之间的空格数不会超过A的长度

证明:

1.如果不是前后缀的话,那开头和结尾就没法涂了

2.由模板串的定义显然

3.画个图或者看样例,喷涂的时候模板串必须相邻或重叠

由性质1,我们发现满足条件的模板串一定是1~next[n],或next[next[n]],next[next[next[n]]]...这样的子串。

因此我们可以枚举前缀的长度,然后\(O(n)\)判定,但这样的复杂度仍是\(O(n^2)\)的,考虑优化


这时就需要用到fail树。

fail树其实就是把失配的位置连起来得到的一棵树。我们对于\(i \in [1,n]\),连边\((next[i],i)\),\(next[i]\)为\(i\)的父亲。由于\(next[i]<i\),连出来的一定是一棵树

显然fail树的根为0,fail树上的节点x表示文本串中1x位,长度为x的子串。而1next[n],或next[next[n]],next[next[next[n]]]...这样的子串其实就是fail树上0~next[n]的一条链。

fail树的性质:

y代表的子串的一个公共前后缀为x代表的子串,当且仅当点x是点y的祖先

因此我们就可以先把0next[n]的链上的节点标记,从根对fail树dfs。每次选在链上的儿子节点递归,然后删除其他儿子节点子树里的所有节点。用一个初始时为1n的链的双向链表维护相邻元素之间的距离(初始距离为1,删掉x,y中间的一个节点z,x,y之间的距离变为dist(x,z)+dist(y,z))。

容易发现这个距离就是模板串在文本串中的匹配位置之间的空格数.根据模板串的性质3,空格数不会超过模板串的长度。而节点x又表示长度为x的子串,因此递归到$x \geq 空格数 $的时候,x就是最小长度.

由于每个节点最多被删除一次,时间复杂度\(O(n)\)


广告:

这道题思路完全不同的做法

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 500000
using namespace std;
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int sz=1;
int head[maxn+5];
void add_edge(int u,int v){
sz++;
E[sz].from=u;
E[sz].to=v;
E[sz].next=head[u];
head[u]=sz;
} int n;
char s[maxn+5];
int nex[maxn+5]; struct list{
int pre[maxn+5],nex[maxn+5];
int mv;
void ini(int n){
mv=1;
for(int i=1;i<=n;i++){
pre[i]=i-1;
nex[i]=i+1;
}
}
void del(int x){
pre[nex[x]]=pre[x];
nex[pre[x]]=nex[x];
mv=max(mv,nex[x]-pre[x]);
pre[x]=nex[x]=0;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
del(y);
}
}
inline int query(){
return mv;
}
}S;
int ans=0;
bool mark[maxn+5];
void dfs(int x){//x实际上是某个nex,代表前缀(模板串)长度
int to;
if(S.query()<=x){//模板串性质3
ans=x;
return;
}
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(mark[y]) to=y;
else S.del(y);//根据fail树的性质2,把不能匹配的部分去掉,得到两个匹配位置之间的最大距离
}
dfs(to);
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=2,j=0;i<=n;i++){
while(j>0&&s[j+1]!=s[i]) j=nex[j];
if(s[j+1]==s[i]) j++;
nex[i]=j;
}
for(int i=1;i<=n;i++) add_edge(nex[i],i);//建立fail树
S.ini(n);
for(int i=n;i>0;i=nex[i]) mark[i]=1;//模板串性质1
dfs(0);
printf("%d\n",ans);
}

[BZOJ 1535] [Luogu 3426]SZA-Template (KMP+fail树+双向链表)的更多相关文章

  1. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

  2. bzoj 2746: [HEOI2012]旅行问题 AC自动机fail树

    2746: [HEOI2012]旅行问题 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 489  Solved: 174[Submit][Status ...

  3. BZOJ 2085 luogu P3502 [POI2010]Hamsters (KMP、Floyd、倍增)

    数组开小毁一生-- 题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2085 这题在洛谷上有个条件是"互不包含",其实 ...

  4. luogu 3426题解 (KMP)

    题面 Byteasar 想在墙上涂一段很长的字符,他为了做这件事从字符的前面一段中截取了一段作为模版. 然后将模版重复喷涂到相应的位置后就得到了他想要的字符序列.一个字符可以被喷涂很多次,但是一个位置 ...

  5. Bzoj 2286 & Luogu P2495 消耗战(LCA+虚树+欧拉序)

    题面 洛谷 Bzoj 题解 很容易想到$O(nk)$的树形$dp$吧,设$f[i]$表示处理完这$i$颗子树的最小花费,同时再设一个$mi[i]$表示$i$到根节点$1$路径上的距离最小值.于是有: ...

  6. BZOJ 2434 阿狸的打字机(fail树)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2434 题意:阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28 ...

  7. BZOJ 5168 && Luogu P3740 [HAOI2014]贴海报 线段树~~

    据说某谷数据十分水...但幸好BZOJ上也过了...话说我记得讲课时讲的是奇奇怪怪的离散化..但现在突然觉得什么都可以线段树瞎搞了...QAQ 直接就是这个区间有没有被覆盖,被覆盖直接return: ...

  8. BZOJ.1535.[POI2005]SZA-Template(KMP DP)

    BZOJ 洛谷 \(Description\) 给定一个字符串\(s\),求一个最短的字符串\(t\)满足,将\(t\)拼接多次后,可以得到\(s\).拼接是指,可以将\(t\)放在当前串的任意位置, ...

  9. BZOJ 4044 Luogu P4762 [CERC2014]Virus Synthesis (回文自动机、DP)

    好难啊..根本不会做..基本上是抄Claris... 题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4044 (luogu) ...

随机推荐

  1. 【清华集训2016】Alice和Bob又在玩游戏

    不难的题目.因为SG性质,所以只需要对一棵树求出. 然后如果发现从上往下DP不太行,所以从下往上DP. 考虑一个点对子树的合并,考虑下一个删的点在哪一个子树,那么剩下的状态实际上就是把一个子树所有能达 ...

  2. jQuery_获取html代码以及更改内容

    代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...

  3. 智能指针之shared_ptr基本概述

    1.shared_ptr允许有多个指针指向同一个对象,unique_ptr独占所指向的对象. 2.类似于vector,智能指针也是模板.创建智能指针: shared_ptr<string> ...

  4. latex参考文献中作者名字含有特殊字符怎么办

  5. JavaBean,EJB,POJO,Spring Bean 的演进历程

    JavaBean Sun公司对类提出的规范:1,类是public的2,有一个无参构造方法3,属性修饰要用private,通过get set操作4,实现Serializable接口5,对事件使用Swin ...

  6. Django日志的配置

    做开发离不开日志,以下是我在工作中写Django项目常用的logging配置.   BASE_LOG_DIR = os.path.join(BASE_DIR, "log") LOG ...

  7. linux下文件权限更改(转载)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_33571752/article/d ...

  8. XMLHttpRequest 对象属性参数参考

    readyState:提供当前 HTML 的就绪状态. readyState可能返回的值: 0:请求未初始化(还没有调用 open()). 1:请求已经建立,但是还没有发送(还没有调用 send()) ...

  9. 解决phpmyadmin出现: Maximum execution time of 300

    在mysql用phpmyadmin导入数据的时候出现: Fatal error: Maximum execution time of 300 seconds exceeded in ... 上网查了很 ...

  10. python连redis测试

    python 版本 3.x执行环境需要安装redis模块: pip install redis 执行脚本前,有redis-cli中查询key值: 执行脚本: ********************* ...