【学习笔记】CF700E Cool Slogans

news/2024/10/17 22:20:47/

服了,做这道题还要重新学一遍 S A M SAM SAM,泪目了。

先不考虑复杂度,考虑一段子串 [ i , j ] [i,j] [i,j]对应的答案。尽量将问题往 S A M SAM SAM上去靠,发现子串 [ i , j ] [i,j] [i,j]对应的就是从 t 0 t_0 t0出发的一条链。假设转移过去的子串是 [ i ′ , j ′ ] [i',j'] [i,j],如果 j ′ = j j'=j j=j那么转移到的点一定是后缀树上的祖先节点,总之这个转移可以提前预处理出来。如果 j ′ ≠ j j'\ne j j=j那么相当于就是在链上取 max ⁡ \max max。又因为 S A M SAM SAM的节点个数是线性的所以直接 D P DP DP,这道题就做完了。

这是我做这道题的第一想法,但是其实我们可以再冷静的分析一步。先不考虑交叉的情况,简单画一个图可以发现最优方案中 s i − 1 s_{i-1} si1一定是 s i s_i si的后缀。这样我们就不用管建出来的 D A G DAG DAG,只需要考虑后缀树上的情形了,因为后缀树对应后缀链接。

比较让人纠结的地方在于,同一等价类对应的答案相同吗?这就涉及到我们对 S A M SAM SAM基本性质的了解了。抛出一个结论:设 s s s是节点 u u u对应的最长串, v v v u u u的祖先,那么 v v v对应的所有字符串在 s s s中的匹配情况相同。证明的关键在于利用 endpos \text{endpos} endpos集合相同。感觉这个证明脑补一下应该不难就略过了。这也说明了一件事,对后缀性质的充分理解才是解决问题的关键。

最后是关于实现。直接暴力二分的复杂度是 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)。但是因为是在树上所以能做的操作还是比较多的,比如说这道题就可以根据父亲的信息来计算,注意到一条链上的 D P DP DP值是连续的,并且相邻两个位置的 D P DP DP值不超过 1 1 1,因此可以做到线性。这个地方的处理还是比较巧妙的。复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

就是模板有点长。

打了一发代码,感觉最恶心的地方还是在于在 S A M SAM SAM上维护一些比较繁琐的信息。如果对 S A M SAM SAM不熟悉的话还是很容易搞错的。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
#define uint unsigned int
using namespace std;
const int N=4e5+5;
const int M=N*20;
int n,tot,last,rt[N],nums[N],cnt,degree[N],ls[M],rs[M],totnodes,dp[N],fr[N],res;
string s;
queue<int>Q;
struct node{int link,len,ch[26],pos;
}t[N];
void ins(int &rt,int l,int r,int x){if(!rt)rt=++totnodes;if(l==r)return;int mid=l+r>>1;x<=mid?ins(ls[rt],l,mid,x):ins(rs[rt],mid+1,r,x);
}
void extend(int c,int x){int cur=++tot;t[cur].len=t[last].len+1;t[cur].pos=x;int p=last;while(p!=-1&&!t[p].ch[c]){t[p].ch[c]=cur;p=t[p].link;}if(p==-1){t[cur].link=0;}else{//assert(t[p].ch[c]);int q=t[p].ch[c];assert(q);if(t[q].len==t[p].len+1){t[cur].link=q;}else{int clone=++tot;t[clone].link=t[q].link,t[clone].len=t[p].len+1;for(int i=0;i<26;i++)t[clone].ch[i]=t[q].ch[i];t[clone].pos=x;t[cur].link=clone;t[q].link=clone;while(p!=-1&&t[p].ch[c]==q){t[p].ch[c]=clone;p=t[p].link;} }}last=cur;ins(rt[cur],0,n-1,x);
}
int merge(int p,int q,int l,int r){if(l>r)return 0;if(!p||!q)return p+q;int o=++totnodes,mid=l+r>>1;ls[o]=merge(ls[p],ls[q],l,mid);rs[o]=merge(rs[p],rs[q],mid+1,r);return o;
}
int query(int p,int l,int r,int ql,int qr){if(!p)return 0;if(ql<=l&&r<=qr)return 1;int mid=l+r>>1;if(ql<=mid&&query(ls[p],l,mid,ql,qr))return 1;if(mid<qr&&query(rs[p],mid+1,r,ql,qr))return 1;return 0;
}
signed main(){//freopen("data.in","r",stdin);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>s;t[0].link=-1;for(int i=0;i<n;i++){extend(s[i]-'a',i);}for(int i=1;i<=tot;i++)degree[t[i].link]++;for(int i=1;i<=tot;i++){if(degree[i]==0){Q.push(i);}}while(Q.size()){int u=Q.front(),v=t[u].link;Q.pop();nums[++cnt]=u;if(v){rt[v]=merge(rt[v],rt[u],0,n-1);if(--degree[v]==0)Q.push(v);}}for(int i=1;i<=tot;i++)assert(degree[i]==0);assert(cnt==tot);for(int i=cnt;i>=1;i--){int x=nums[i],y=t[x].link;if(fr[y]==0||query(rt[fr[y]],0,n-1,t[x].pos-t[x].len+t[fr[y]].len,t[x].pos-1)){dp[x]=dp[fr[y]]+1;fr[x]=x;}else{dp[x]=dp[fr[y]];fr[x]=fr[y];}res=max(res,dp[x]);}cout<<res;
}

http://www.ppmy.cn/news/662922.html

相关文章

面试系列(一):完美世界 C++服务器开发

一面 1.自我介绍 2.TCP/IP&#xff1f; 3.堆&#xff0c;是用来解决什么的&#xff1f; 4.用C写代码多吗&#xff1f;代码量有多少&#xff1f; 5.平时的学习方式&#xff1f; 6.冒泡排序思想&#xff1f; 7.在项目中都用过哪些STL容器&#xff1f; vector和list的区别…

2020完美世界暑期实习面经

2020完美世界暑期实习面经 本人末流985 数字媒体技术 大三学生&#xff0c;今天参加了完美世界暑期实习生游戏客户端一面&#xff0c;面试官是一个技术小哥吧应该是&#xff0c;略显敷衍&#xff0c;总结一下。 一面 自我介绍项目都做啥功能了什么叫虚函数指针和引用的区别对…

完美世界面试经历

面试没过基础不是特别好总结一下 一面 问&#xff1a; 0xFF&#xff0c;0x8F&#xff0c;0x7F 换成单字节有符号十进制 问&#xff1a;用过stl么写个map迭代器删除偶数元素 问&#xff1a;一个金条给工人工钱&#xff0c;7天&#xff0c;一天一给&#xff0c;给多了工人就…

完美世界-游戏Java开发工程师-一面

时间&#xff1a;2017-03-22 时长&#xff1a;19分 类型&#xff1a;内推笔试通过后一面 虽然在大学阶段对于J2EE学习比较多&#xff0c;但是也非常喜欢打游戏啊&#xff01;而且这是完美世界&#xff01;所以选择投了游戏开发岗&#xff0c;也许是一开始的时候说了自己原来…

完美世界-Java游戏开发-二面

时间&#xff1a;2017-03-30 时长&#xff1a;15分 类型&#xff1a;二面 面试官比较聊得来&#xff0c;人比较和善&#xff0c;游戏面试还是nice的&#xff0c;老铁 1. 自我介绍 2. 平时玩哪些游戏&#xff1f;端游、页游 3. Maven你是怎么使用的&#xff1f; 4. 对于qu…

完美世界 面试

完美校招只分三个职位 c 、java、 游戏策划 我面的C,问的问题偏基础吧. 一、给出一个十六进制的数0xFF 0x80 &#xff08;只有2“位”&#xff09; 将其转换成有符号的一字节的十进制整数 解&#xff1a;因为是转成有符号数 所以 可以先将其转成二进制 如&#xff1a;0xF…

完美世界手游如何在电脑上玩 完美世界手游模拟器教程

十三年经典历久弥新&#xff0c;青春记忆永不落幕.《完美世界手游》由腾讯与完美世界首次合作&#xff0c;东西方融合震撼奇幻风格&#xff0c;次世代标准画质&#xff0c;完美还原端游经典&#xff0c;十三年的积累&#xff0c;只为这一刻的惊艳亮相。接下来&#xff0c;和小编…

2014完美世界校招笔试题及答案

明天要去完美笔试的缘故&#xff0c;特意在网上找了完美2014年的校招笔试题练手。可能是自己基础比较薄弱的缘故吧&#xff0c;感觉笔试题还是很难的。答案都是我自己上网查资料做的&#xff0c;有不对的地方还希望小伙伴们能够指正。选择题第13题和最后一道编程题&#xff0c;…