CF700E Cool Slogans(SAM,dp)
解析
好题。
首先,我们每次都令 sis_isi 是 si+1s_{i+1}si+1 的后缀,肯定是不劣的
问题就可以转化到 fail 树上了
首先肯定要线段树合并处理出endpos集合
朴素想法:设父亲 fafafa 的结束位置为 posfapos_{fa}posfa,若 [posfa−lenfa+1,posfa][pos_{fa}-len_{fa}+1,pos_{fa}][posfa−lenfa+1,posfa] 中有两个儿子 sss,就令 ansfa+1→anssans_{fa}+1\to ans_{s}ansfa+1→anss。
但是仔细想想还会有一些问题
比如,可能会跳两次才可以转移,但是连续的父子之间都无法构成二倍关系。这个不麻烦,记录一下上一次成功转移的 preprepre,每次判断 [pospre−lenpre+1,pospre][pos_{pre}-len_{pre}+1,pos_{pre}][pospre−lenpre+1,pospre] 即可
还有一个问题,我们这里的 lenlenlen 都是最长长度,会不会有一个同一等价类里较短的子串可以转移,而那个最长子串无法转移呢?
不会
考虑反证证明:
假设有子串:
- A
- cA
- AA
其中1、2属于同一等价类,2无法转移到3而1可以
那么必然有串:cAA(这样12才能是一个等价类)
又由于3是该等价类最长字符串,所以AA和cAA不属于同一等价类
也就是说有一个位置出现了AA却没有出现cAA,那么这个地方(第一个A的位置)也会出现了A却没有出现cA
所以1、2不在一个等价类里,与题设矛盾,原命题得证
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=1e9+7;
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
int n,m;int ed[N];
struct SAM{struct node{int len,fa;int tr[26];}st[N];int lst=1,tot=1;int pl[N];inline void ins(int c,int o){c-='a';int cur=++tot,p=lst;lst=tot;ed[cur]=o;st[cur].len=st[p].len+1;for(;p&&!st[p].tr[c];p=st[p].fa) st[p].tr[c]=cur;if(!st[p].tr[c]) st[cur].fa=1;else{int q=st[p].tr[c];if(st[q].len==st[p].len+1) st[cur].fa=q;else{int pp=++tot;st[pp]=st[q];ed[pp]=ed[q];st[pp].len=st[p].len+1;st[cur].fa=st[q].fa=pp;for(;p&&st[p].tr[c]==q;p=st[p].fa) st[p].tr[c]=pp;}}}void print(){for(int i=1;i<=tot;i++){printf("i=%d fa=%d len=%d ed=%d\n",i,st[i].fa,st[i].len,ed[i]);}putchar('\n');return;}
}s;struct segmentTree{#define mid ((l+r)>>1)struct tree{int ls,rs,siz;}tr[N<<6];int rt[N],tot;inline int copy(int x){tr[++tot]=tr[x];return tot;}inline void pushup(int k){tr[k].siz=tr[tr[k].ls].siz+tr[tr[k].rs].siz;return;}void add(int &k,int l,int r,int p){if(!k) k=copy(0);if(l==r){tr[k].siz++;return;}if(p<=mid) add(tr[k].ls,l,mid,p);else add(tr[k].rs,mid+1,r,p);pushup(k);}int merge(int a,int b,int l,int r){if(!a||!b) return a|b;a=copy(a);if(l==r){tr[a].siz+=tr[b].siz;return a;}tr[a].ls=merge(tr[a].ls,tr[b].ls,l,mid);tr[a].rs=merge(tr[a].rs,tr[b].rs,mid+1,r);pushup(a);return a;}int ask(int k,int l,int r,int x,int y){if(x>y) return 0;if(!k) return 0;if(x<=l&&r<=y) return tr[k].siz;int res(0);if(x<=mid) res+=ask(tr[k].ls,l,mid,x,y);if(y>mid) res+=ask(tr[k].rs,mid+1,r,x,y);return res;}
}t;char ss[N];vector<int>v[N];
int dp[N],pre[N];
void dfs1(int x){if(ed[x]) t.add(t.rt[x],1,n,ed[x]);for(int to:v[x]){//printf("%d -> %d\n",x,to);dfs1(to);t.rt[x]=t.merge(t.rt[x],t.rt[to],1,n);}return;
}
int ans;
void dfs2(int x){//printf("x=%d dp=%d pre=%d\n",x,dp[x],pre[x]);ans=max(ans,dp[x]);for(int to:v[x]){//printf(" %d -> %d +1? (%d %d)\n",x,to,ed[to]-s.st[to].len+s.st[pre[x]].len,ed[to]-1);if(x==1||(ed[to]&&t.ask(t.rt[pre[x]],1,n,ed[to]-s.st[to].len+s.st[pre[x]].len,ed[to]-1))){dp[to]=dp[x]+1;pre[to]=to;}else dp[to]=dp[x],pre[to]=pre[x];dfs2(to);}return;
}
signed main() {#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endif
//printf("%d\n",sizeof(t)/1024/1024);n=read();scanf(" %s",ss+1);for(int i=1;i<=n;i++) s.ins(ss[i],i);//s.print();for(int i=2;i<=s.tot;i++) v[s.st[i].fa].push_back(i);dfs1(1);dfs2(1);printf("%d\n",ans);return 0;
}
/*
21
abcaaaaabbcbacbabcbcb*/
CF700E Cool Slogans(SAM,dp)相关推荐
- [NOIP1996 提高组] 挖地雷(C++,DP)
题目描述 在一个地图上有NNN个地窖(N≤20)(N \le 20)(N≤20),每个地窖中埋有一定数量的地雷.同时,给出地窖之间的连接路径.当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷, ...
- 牛客练习赛24题解(搜索,DP)
A题,C题不讲,基础题(但是我要抨击一下这次比赛,卡cin,cout,卡的太狠了,根本就不让过的那种,QAQ) 链接:https://www.nowcoder.com/acm/contest/157/ ...
- 【BZOJ3676】 [Apio2014]回文串(SAM,manacher)
传送门 BZOJ 洛谷 Solution 考虑我们每找到一个回文串就更新一次答案,跑个SAM,这样子复杂度是爆炸的. 接下来的就是优化: 我们可以倍增跳直到跳不了,最后的siz就是出现次数. 没了?没 ...
- 51nod (2534,dp)
51nod 2534 题解:本题我们定义dp[i][0]表示两个人当前都走过前i个点并且最后一个点第一个人走两个i中的其中一个位置的总和最小值,dp[i][1]类似.具体实现看代码 #include& ...
- 【CodeForces - 361D】Levko and Array (二分,dp)
题干: Levko has an array that consists of integers: a1, a2, ... , an. But he doesn't like this array a ...
- 程序员面试金典 - 面试题 01.05. 一次编辑(编辑距离,DP)
1. 题目 字符串有三种编辑操作:插入一个字符.删除一个字符或者替换一个字符. 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑. 示例 1: 输入: first = "p ...
- 【oiClass 2085】马(排序,DP)
题目描述 战神阿瑞斯听说2008年在中华大地上,将举行一届规模盛大的奥林匹克运动会,心中顿觉异常兴奋,他召集了n位天将,骑着他们自己的天马,准备在广阔的天空上,举行一场空前的天马天将列队表演.天马在熟 ...
- 洛谷4965 薇尔莉特的打字机(Trie,DP)
神仙题. 考虑在一棵 Trie 上进行染色,将可能出现的串的末尾染成黑色.答案就是黑点的个数.一开始只有 \(A\) 的末尾点是黑色. 当出现一个字符(不是退格)\(c\) 时,就要将每个黑点的 \( ...
- CF1540B Tree Array(期望,dp)
解析 关于合理的实现 这题卡在最后的小破dp是我没想到的 一开始看到200的数据范围就不禁笑出了声 lca直接On求! 然后就开始大力分类讨论 然后就卡在了一个问题上 两个栈AB,分别有a和b个元素, ...
最新文章
- 设计模式第六课 单例模式
- Mac下php 5升级到php 7的步骤详解
- 【题解】luogu p1111 修复公路
- 读空气java_空气质量指数查询示例代码
- sql limit 子句_Java 8流中的常见SQL子句及其等效项
- 虚拟实验工场大学计算机实验报告答案,虚拟实验实验报告 - 实验报告 - 书业网.doc...
- 第一章 处理器体系结构
- shell脚本的简单学习
- win10 .net framework 3.5 安装报错 0x800F0954问题
- OpenShift 4 - DevSecOps Workshop (15) - 利用OpenShift GitOps向多个目标部署应用
- cut最后几位 shell_shell命令_cut
- 用Nginx搭建IIS集群实现负载均衡
- zookeeper选举机制及相关概念
- 转:Oracle数据库一致性读的原理(Consistent Read)
- ASP.NET生成eurl.axd Http异常错误处理方法【转】
- CarMaker试用版许可证申请与软件安装过程
- Java设计模式及类图
- 佛系前端面试题记录--第五周
- 3:AngularJS:模糊查询过滤内容,下拉菜单排序,过滤敏感字符,验证判断后添加表格信息
- Kruskal理解+代码解析
热门文章
- 2018年最值得关注的15大技术趋势,区块链将得到更广泛的应用
- java foreach 跳过本次循环_【Java】对foreach循环的思考
- bread是可数还是不可数_雅思官方语法教程之——这个名词到底可不可数?
- python bind sock_python SOCKET编程详细介绍
- mysql动态扩展_动态可扩展查询MYSQL5.7JSON+虚拟列+Mybatis
- c语言修仙受控可看吗,强推三本神奇到爆的小说,c语言修仙,程序员与修真会擦出什么火花...
- leetcode343. 整数拆分(思路+详解)
- 洛谷 P1122 最大子树和-求树的最大子树权值和
- [蓝桥杯2015决赛]积分之迷-枚举(水题)
- [蓝桥杯2018决赛]迷宫与陷阱