解析

好题。
首先,我们每次都令 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 都是最长长度,会不会有一个同一等价类里较短的子串可以转移,而那个最长子串无法转移呢?
不会
考虑反证证明:
假设有子串:

  1. A
  2. cA
  3. 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)相关推荐

  1. [NOIP1996 提高组] 挖地雷(C++,DP)

    题目描述 在一个地图上有NNN个地窖(N≤20)(N \le 20)(N≤20),每个地窖中埋有一定数量的地雷.同时,给出地窖之间的连接路径.当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷, ...

  2. 牛客练习赛24题解(搜索,DP)

    A题,C题不讲,基础题(但是我要抨击一下这次比赛,卡cin,cout,卡的太狠了,根本就不让过的那种,QAQ) 链接:https://www.nowcoder.com/acm/contest/157/ ...

  3. 【BZOJ3676】 [Apio2014]回文串(SAM,manacher)

    传送门 BZOJ 洛谷 Solution 考虑我们每找到一个回文串就更新一次答案,跑个SAM,这样子复杂度是爆炸的. 接下来的就是优化: 我们可以倍增跳直到跳不了,最后的siz就是出现次数. 没了?没 ...

  4. 51nod (2534,dp)

    51nod 2534 题解:本题我们定义dp[i][0]表示两个人当前都走过前i个点并且最后一个点第一个人走两个i中的其中一个位置的总和最小值,dp[i][1]类似.具体实现看代码 #include& ...

  5. 【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 ...

  6. 程序员面试金典 - 面试题 01.05. 一次编辑(编辑距离,DP)

    1. 题目 字符串有三种编辑操作:插入一个字符.删除一个字符或者替换一个字符. 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑. 示例 1: 输入: first = "p ...

  7. 【oiClass 2085】马(排序,DP)

    题目描述 战神阿瑞斯听说2008年在中华大地上,将举行一届规模盛大的奥林匹克运动会,心中顿觉异常兴奋,他召集了n位天将,骑着他们自己的天马,准备在广阔的天空上,举行一场空前的天马天将列队表演.天马在熟 ...

  8. 洛谷4965 薇尔莉特的打字机(Trie,DP)

    神仙题. 考虑在一棵 Trie 上进行染色,将可能出现的串的末尾染成黑色.答案就是黑点的个数.一开始只有 \(A\) 的末尾点是黑色. 当出现一个字符(不是退格)\(c\) 时,就要将每个黑点的 \( ...

  9. CF1540B Tree Array(期望,dp)

    解析 关于合理的实现 这题卡在最后的小破dp是我没想到的 一开始看到200的数据范围就不禁笑出了声 lca直接On求! 然后就开始大力分类讨论 然后就卡在了一个问题上 两个栈AB,分别有a和b个元素, ...

最新文章

  1. 设计模式第六课 单例模式
  2. Mac下php 5升级到php 7的步骤详解
  3. 【题解】luogu p1111 修复公路
  4. 读空气java_空气质量指数查询示例代码
  5. sql limit 子句_Java 8流中的常见SQL子句及其等效项
  6. 虚拟实验工场大学计算机实验报告答案,虚拟实验实验报告 - 实验报告 - 书业网.doc...
  7. 第一章 处理器体系结构
  8. shell脚本的简单学习
  9. win10 .net framework 3.5 安装报错 0x800F0954问题
  10. OpenShift 4 - DevSecOps Workshop (15) - 利用OpenShift GitOps向多个目标部署应用
  11. cut最后几位 shell_shell命令_cut
  12. 用Nginx搭建IIS集群实现负载均衡
  13. zookeeper选举机制及相关概念
  14. 转:Oracle数据库一致性读的原理(Consistent Read)
  15. ASP.NET生成eurl.axd Http异常错误处理方法【转】
  16. CarMaker试用版许可证申请与软件安装过程
  17. Java设计模式及类图
  18. 佛系前端面试题记录--第五周
  19. 3:AngularJS:模糊查询过滤内容,下拉菜单排序,过滤敏感字符,验证判断后添加表格信息
  20. Kruskal理解+代码解析

热门文章

  1. 2018年最值得关注的15大技术趋势,区块链将得到更广泛的应用
  2. java foreach 跳过本次循环_【Java】对foreach循环的思考
  3. bread是可数还是不可数_雅思官方语法教程之——这个名词到底可不可数?
  4. python bind sock_python SOCKET编程详细介绍
  5. mysql动态扩展_动态可扩展查询MYSQL5.7JSON+虚拟列+Mybatis
  6. c语言修仙受控可看吗,强推三本神奇到爆的小说,c语言修仙,程序员与修真会擦出什么火花...
  7. leetcode343. 整数拆分(思路+详解)
  8. 洛谷 P1122 最大子树和-求树的最大子树权值和
  9. [蓝桥杯2015决赛]积分之迷-枚举(水题)
  10. [蓝桥杯2018决赛]迷宫与陷阱