RemoteJudge

又是一道用线段树合并来维护\(endpos\)的题,还有一道见我的博客CF666E

思路

先把\(SAM\)建出来

如果两个相邻的串\(s_i\)和\(s_{i+1}\)要满足\(s_i\)在\(s_{i+1}\)中至少出现了两次,那么\(s_i\)显然是\(s_{i+1}\)对应的结点在\(parent\ tree\)上的祖先,那么我们可以在\(parent\ tree\)上树形dp来得出答案

转移自顶向下进行,\(s_i\)在\(s_{i+1}\)中至少出现了两次意味着\(s_i\)在\(s_{i+1}\)的所有\(endpos\)位置都出现了两次,所以我们只需要知道\(s_{i+1}\)的\(endpos\)中任意一个元素并结合线段树来判断能否从\(s_i\)向\(s_{i+1}\)转移。我直接维护了一个\(firstpos\)代表\(endpos\)中的最小值

最后注意不能转移时需要把值继承过来

代码

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <map>
#include <set> using namespace std; #define IINF 0x3f3f3f3f3f3f3f3fLL
#define ull unsigned long long
#define pii pair<int, int>
#define uint unsigned int
#define mii map<int, int>
#define lbd lower_bound
#define ubd upper_bound
#define INF 0x3f3f3f3f
#define vi vector<int>
#define ll long long
#define mp make_pair
#define pb push_back #define N 200000 int n;
char s[N+5]; struct SAM {
int nxt[26][2*N+5], maxlen[2*N+5], link[2*N+5], firstpos[2*N+5], tot, lst;
int sumv[100*N+5], ch[2][100*N+5], root[2*N+5], nid;
vi G[2*N+5];
int top[2*N+5];
ll f[2*N+5], ans;
void init() {
tot = lst = 1;
nid = 0;
}
void pushup(int o) {
sumv[o] = sumv[ch[0][o]]+sumv[ch[1][o]];
}
void add(int &o, int l, int r, int x) {
if(!o) o = ++nid;
if(l == r) {
sumv[o] = 1;
return ;
}
int mid = (l+r)>>1;
if(x <= mid) add(ch[0][o], l, mid, x);
else add(ch[1][o], mid+1, r, x);
pushup(o);
}
int merge(int o, int u, int l, int r) {
if(!o || !u) return o|u;
int v = ++nid;
if(l == r) {
sumv[v] = sumv[o]+sumv[u] ? 1 : 0;
return v;
}
int mid = (l+r)>>1;
ch[0][v] = merge(ch[0][o], ch[0][u], l, mid);
ch[1][v] = merge(ch[1][o], ch[1][u], mid+1, r);
pushup(v);
return v;
}
int query(int o, int l, int r, int L, int R) {
if(!o) return 0;
if(L <= l && r <= R) return sumv[o];
int ret = 0, mid = (l+r)>>1;
if(L <= mid) ret += query(ch[0][o], l, mid, L, R);
if(R > mid) ret += query(ch[1][o], mid+1, r, L, R);
return ret;
}
void extend(int c, int pos) {
int cur = ++tot;
maxlen[cur] = maxlen[lst]+1;
firstpos[cur] = pos;
while(lst && !nxt[c][lst]) nxt[c][lst] = cur, lst = link[lst];
if(!lst) link[cur] = 1;
else {
int p = lst, q = nxt[c][p];
if(maxlen[q] == maxlen[p]+1) link[cur] = q;
else {
int clone = ++tot;
maxlen[clone] = maxlen[p]+1;
link[clone] = link[q], link[q] = link[cur] = clone;
firstpos[clone] = firstpos[q];
for(int i = 0; i < 26; ++i) nxt[i][clone] = nxt[i][q];
while(p && nxt[c][p] == q) nxt[c][p] = clone, p = link[p];
}
}
lst = cur;
}
void dfs(int u) {
for(int i = 0, v; i < G[u].size(); ++i) {
v = G[u][i];
dfs(v);
root[u] = merge(root[u], root[v], 1, n);
}
}
void build() {
init();
for(int i = 1; i <= n; ++i) {
add(root[tot+1], 1, n, i);
extend(s[i]-'a', i);
}
for(int i = 2; i <= tot; ++i) G[link[i]].pb(i);
dfs(1);
}
void dp(int u) { // top数组用来辅助转移
for(int i = 0, v; i < G[u].size(); ++i) {
v = G[u][i];
if(u == 1) f[v] = 1, top[v] = v;
else {
if(query(root[top[u]], 1, n, firstpos[v]-maxlen[v]+maxlen[top[u]], firstpos[v]) >= 2) f[v] = f[u]+1, top[v] = v;
else f[v] = f[u], top[v] = top[u];
}
ans = max(ans, f[v]);
dp(v);
}
}
ll getans() {
dp(1);
return ans;
}
}sa; int main() {
scanf("%d", &n);
scanf("%s", s+1);
sa.build();
printf("%lld\n", sa.getans());
return 0;
}

CF700E Cool Slogans——SAM+线段树合并的更多相关文章

  1. CF700E:Cool Slogans(SAM,线段树合并)

    Description 给你一个字符串,如果一个串包含两个可有交集的相同子串,那么这个串的价值就是子串的价值+1.问你给定字符串的最大价值子串的价值. Input 第一行读入字符串长度$n$,第二行是 ...

  2. Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...

  3. CF700E-Cool Slogans【SAM,线段树合并,dp】

    正题 题目链接:https://www.luogu.com.cn/problem/CF700E 题目大意 给出一个字符串\(S\),求一个最大的\(k\)使得存在\(k\)个字符串其中\(s_1\)是 ...

  4. CF1037H Security——SAM+线段树合并

    又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...

  5. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  6. UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...

  7. loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增

    题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...

  8. 2019.02.27 bzoj4556: [Tjoi2016&Heoi2016]字符串(二分答案+sam+线段树合并)

    传送门 题意:给一个字符串SSS. 有mmm次询问,每次给四个参数a,b,c,da,b,c,da,b,c,d,问s[a...b]s[a...b]s[a...b]的所有子串和s[x...y]s[x... ...

  9. [NOI2018]你的名字(SAM+线段树合并)

    考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...

随机推荐

  1. Reactor系列(十)collectMap集合

    #java#reactor#collect#hashMap# 转换成Map 视频讲解: https://www.bilibili.com/video/av80048104/ FluxMonoTestC ...

  2. tesseract 3.04在centos6上安装

    tesseract是一个开源的OCR文字识别工具 查找相关文章:tesseract   tesseract 4.0一直安装失败,后来参照网上的方法,成功安装3.04 1 2 3 4 5 6 7 8 9 ...

  3. JAVA日常之四

    构造函数 又称“构建器”,函数名称与类名称完全相同,无返回值. 每个类都有构造函数. 可以自定义构造函数,并且可以创建多个重载/过载的构造函数. 若没有手动创建该函数,总会存在一个默认的构造函数(无参 ...

  4. Web安全小结之前端

  5. python — 装饰器、迭代器

    目录 1 装饰器 2 迭代器 3 可迭代对象 1 装饰器 1.1目的.应用场景: 目的: 在不改变原函数内部代码的基础上,在函数执行前后自定义功能. 应用场景: 想要为函数扩展功能时,可以选择用装饰器 ...

  6. SpringBoot热启动让开发更便捷

    在开发过程中,当写完一个功能我们需要运行应用程序测试,可能这个小功能中存在多个小bug,我们需要改正后重启服务器,这无形之中拖慢了开发的速度增加了开发时间,SpringBoot提供了spring-bo ...

  7. luogu1156垃圾陷阱题解--背包DP

    题目链接 https://www.luogu.org/problemnew/show/P1156 方法1 分析 将已经爬的高度看作背包容积,最大剩余血量看作价值,\(f[i][j]\)表示吃完第\(i ...

  8. React会自动把虚拟DOM数组展开

    React会自动把虚拟DOM数组展开,放在父级虚拟DOM中,这个特性还是我同事帮我解决一个问题的时候,偶然发现的. 如何将一个数据数组转换为一个虚拟DOM的数组,需要使用map,如下: const n ...

  9. NPOI 将excel转换为datatable或者将datatable转换为excel

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. # 机器学习算法总结-第五天(降维算法PCA/SVD)