NOI2018 你的名字——SAM+线段树合并
题目大意
有一个串\(S\),每次询问给你一个串\(T\),两个数\(L\)和\(R\),问你\(T\)有多少个本质不同的子串不是\(S[L,R]\)的子串
SOLUTION
如果你做过生成魔咒和CF1037H,就会做这道题了
有两个坑点:
1.线段树合并时必须每次都新建结点,因为两颗树都得保留
2.每次失配时必须先尝试减小已经匹配的长度,无法继续减少时再跳\(suflink\)
我的大常数代码
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <random>
#include <string>
#include <vector>
#include <cmath>
#include <ctime>
#include <queue>
#include <map>
#include <set>
#define IINF 0x3f3f3f3f3f3f3f3fLL
#define u64 unsigned long long
#define pii pair<int, int>
#define mii map<int, int>
#define u32 unsigned 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 is insert
#define se second
#define fi first
#define ps push
#define $SHOW(x) cout << #x" = " << x << endl
#define $DEBUG() printf("%d %s\n", __LINE__, __FUNCTION__)
using namespace std;
#define S 500000
#define Q 100000
#define T 1000000
#define mid ((l+r)>>1)
int q, n, m, L, R;
char s[S+5], t[T+5];
int nid1 = 1, lst1 = 1, ch1[26][2*S+5], link1[2*S+5], len1[2*S+5], nodecnt, root[2*S+5], ch[2][200*S+5];
int nid, lst, nxt[26][2*T+5], link[2*T+5], len[2*T+5], rest[2*T+5], val[T+5];
ll ans = 0;
int a[2*T+5], buc[2*T+5];
void add(int &o, int l, int r, int x) {
if (!o) o = ++nodecnt;
if (l == r) return ;
if (x <= mid) add(ch[0][o], l, mid, x);
else add(ch[1][o], mid+1, r, x);
}
int merge(int x, int y, int l, int r) {
int u = ++nodecnt;
if (x * y == 0) u = x ? x : y;
else {
ch[0][u] = merge(ch[0][x], ch[0][y], l, mid);
ch[1][u] = merge(ch[1][x], ch[1][y], mid + 1, r);
}
return u;
}
int query(int u, int l, int r, int L, int R) {
if (!u) return 0;
if (L <= l && r <= R) return 1;
int ret = 0;
if (L <= mid) ret |= query(ch[0][u], l, mid, L, R);
if (R > mid) ret |= query(ch[1][u], mid+1, r, L, R);
return ret;
}
void build() {
for (int i = 1, c; i <= n; ++i) {
c = s[i] - 'a';
int cur = ++nid1;
len1[cur] = len1[lst1] + 1;
add(root[cur], 1, n, i);
while (lst1 && !ch1[c][lst1]) ch1[c][lst1] = cur, lst1 = link1[lst1];
if (!lst1) link1[cur] = 1;
else {
int p = lst1, q = ch1[c][lst1];
if (len1[q] == len1[p] + 1) link1[cur] = q;
else {
int clone = ++nid1;
len1[clone] = len1[p] + 1;
for (int j = 0; j < 26; ++j) ch1[j][clone] = ch1[j][q];
link1[clone] = link1[q], link1[cur] = link1[q] = clone;
while (p && ch1[c][p] == q) ch1[c][p] = clone, p = link1[p];
}
}
lst1 = cur;
}
for (int i = 1; i <= nid1; ++i) buc[len1[i]]++;
for (int i = 1; i <= n; ++i) buc[i] += buc[i-1];
for (int i = 1; i <= nid1; ++i) a[buc[len1[i]]--] = i;
for (int i = nid1; i >= 2; --i) root[link1[a[i]]] = merge(root[link1[a[i]]], root[a[i]], 1, n);
}
void extend(int c) {
int cur = ++nid;
len[cur] = len[lst] + 1;
for(int i = 0; i < 26; ++i) nxt[i][cur] = 0;
while (lst && !nxt[c][lst]) nxt[c][lst] = cur, lst = link[lst];
if (!lst) link[cur] = 1;
else {
int p = lst, q = nxt[c][lst];
if (len[q] == len[p] + 1) link[cur] = q;
else {
int clone = ++nid;
len[clone] = len[p] + 1;
for (int i = 0; i < 26; ++i) nxt[i][clone] = nxt[i][q];
link[clone] = link[q], link[q] = link[cur] = clone;
while (p && nxt[c][p] == q) nxt[c][p] = clone, p = link[p];
}
}
lst = cur;
}
void calc() {
m = strlen(t+1);
lst = nid = 1;
int i, j, c;
for (i = 0; i < 26; ++i) nxt[i][1] = 0;
for (j = 1; j <= m; ++j) extend(t[j] - 'a');
for (i = 2; i <= nid; ++i) rest[i] = 0;
int u = 1, match = 0;
for (i = 1, c; i <= m; ++i) {
c = t[i] - 'a';
while (u && !query(root[ch1[c][u]], 1, n, L + match, R)) {
if(match > len1[link1[u]]) match--;
else u = link1[u], match = len1[u];
}
if (!u) u = 1, match = 0;
else u = ch1[c][u], match++;
val[i] = match;
}
u = 1;
for (i = 1; i <= m; ++i) u = nxt[t[i] - 'a'][u], rest[u] = val[i];
for (i = 0; i <= m; ++i) buc[i] = 0;
for (i = 1; i <= nid; ++i) buc[len[i]]++;
for (i = 1; i <= m; ++i) buc[i] += buc[i-1];
for (i = 1; i <= nid; ++i) a[buc[len[i]]--] = i;
for (i = nid; i >= 2; --i) rest[link[a[i]]] = max(rest[link[a[i]]], rest[a[i]]);
ll ans = 0;
for (i = 2; i <= nid; ++i) ans += max(0, min(len[i] - rest[i], len[i] - len[link[i]]));
printf("%lld\n", ans);
}
int main() {
scanf("%s", s+1);
n = strlen(s+1);
build();
scanf("%d", &q);
for (int i = 1; i <= q; ++i) {
scanf("%s%d%d", t+1, &L, &R);
calc();
}
return 0;
}
NOI2018 你的名字——SAM+线段树合并的更多相关文章
- [NOI2018]你的名字(SAM+线段树合并)
考虑l=1,r=n的68分,对S和T建SAM,对T的SAM上的每个节点,计算它能给答案带来多少贡献. T上节点x代表的本质不同的子串数为mx[x]-mx[fa[x]],然后需要去掉所代表子串与S的最长 ...
- 【BZOJ5417】[NOI2018]你的名字(线段树,后缀自动机)
[BZOJ5417][NOI2018]你的名字(线段树,后缀自动机) 题面 BZOJ 洛谷 题解 首先考虑\(l=1,r=|S|\)的做法,对于每次询问的\(T\)串,暴力在\(S\)串的\(SAM\ ...
- 【NOI2018】你的名字(SAM & 线段树合并)
Description Hint Solution 不妨先讨论一下无区间限制的做法. 首先"子串"可以理解为"前缀的后缀",因此我们定义一个 \(\lim(i) ...
- UOJ#395. 【NOI2018】你的名字 字符串,SAM,线段树合并
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ395.html 题解 记得同步赛的时候这题我爆0了,最暴力的暴力都没调出来. 首先我们看看 68 分怎么做 ...
- P4770-[NOI2018]你的名字【SAM,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/P4770 题目大意 给出一个长度为\(n\)的字符串\(S\).\(q\)次询问给出一个串\(T\)和一个区间\([ ...
- CF1037H Security——SAM+线段树合并
又是一道\(SAM\)维护\(endpos\)集合的题,我直接把CF700E的板子粘过来了QwQ 思路 如果我们有\([l,r]\)对应的\(SAM\),只需要在上面贪心就可以了.因为要求的是字典序比 ...
- luogu4770 [NOI2018]你的名字 (SAM+主席树)
对S建SAM,拿着T在上面跑 跑的时候不仅无法转移要跳parent,转移过去不在范围内也要跳parent(注意因为范围和长度有关,跳的时候应该把长度一点一点地缩) 这样就能得到对于T的每个前缀,它最长 ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...
随机推荐
- 最新 钢银电商java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.钢银电商等10家互联网公司的校招Offer,因为某些自身原因最终选择了钢银电商.6.7月主要是做系统复习.项目复盘.Leet ...
- 学习笔记:CentOS7学习之十七: Linux计划任务与日志的管理
目录 学习笔记:CentOS7学习之十七: Linux计划任务与日志的管理 17.1 计划任务-at-cron-计划任务使用方法 17.1.1 at计划任务的使用 17.1.2 查看和删除at将要执行 ...
- TypeScript 解构
⒈解构数组 最简单的解构莫过于数组的解构赋值了: let input = [1, 2]; let [first, second] = input; console.log(first); // out ...
- 【转载】Jave开发手册之正则表达式预编译
今天又水一篇,java开发手册华山版 一.编程规约 (九)其它 第一条 解释:Pattern要定义为static final静态变量,以避免执行多次预编译. 错误用法: // 没有使用预编译 priv ...
- LeetCode. 矩阵中的最长递增路径
题目要求: 给定一个整数矩阵,找出最长递增路径的长度. 对于每个单元格,你可以往上,下,左,右四个方向移动. 你不能在对角线方向上移动或移动到边界外(即不允许环绕). 示例: 输入: nums = [ ...
- Web Server 分布式服务: Nginx负载均衡
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler使用.其 ...
- 第六章 ZYNQ-MIZ701 GPIO使用之MIO
6.0 本章难度系数★★☆☆☆☆☆ 6.1 GPIO简介 Zynq7000系列芯片有54个MIO(multiuse I/O),它们分配在 GPIO 的Bank0 和Bank1隶属于PS部分,这些I ...
- Istio技术与实践01: 源码解析之Pilot多云平台服务发现机制
服务模型 首先,Istio作为一个(微)服务治理的平台,和其他的微服务模型一样也提供了Service,ServiceInstance这样抽象服务模型.如Service的定义中所表达的,一个服务有一个全 ...
- Java集合--Hash、Hash冲突
一.Hash 散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构.也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这 ...
- 09 redis中布隆过滤器的使用
我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容.问题来了,新闻客户端推荐系统如何实现推送去重的? 会想到服务器记录了用户看过的所有历史记录,当推 ...