【LOJ】#2720. 「NOI2018」你的名字
题解
把S串建一个后缀自动机
用一个可持久化权值线段树维护每个节点的right集合是哪些节点
求本质不同的子串我们就是要求T串中以每个点为结束点的串有多少在\(S[l..r]\)中出现过
首先我们需要对于T串每个点本身和自己的匹配长度,可以建一个后缀自动机来完成
然后把T串放在S串上跑匹配,匹配到下一个点x时,匹配的长度是len,如果x所在的right集合在\([l + len - 1,r]\)中没有,那么就不合法,把长度减少,如果长度减少到和父亲节点的长度一样,则需要把当前节点跳到父亲节点上
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <random>
#include <ctime>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define MAXN 1000005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) out(x / 10);
putchar('0' + x % 10);
}
char s[MAXN],t[MAXN];
int N,Q,val[MAXN];
int64 ans[MAXN];
struct tr_node {
int lc,rc;
}tr[MAXN * 40];
int Ncnt,rt[MAXN * 2];
int Find(int u,int l,int r,int ql,int qr) {
if(!u) return -1;
if(l > qr || r < ql) return -1;
if(l == r) return l;
int mid = (l + r) >> 1;
int res = Find(tr[u].rc,mid + 1,r,ql,qr);
if(res == -1) res = Find(tr[u].lc,l,mid,ql,qr);
return res;
}
void Insert(int &u,int v,int l,int r,int p) {
u = ++Ncnt;
tr[u] = tr[v];
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) Insert(tr[u].lc,tr[v].lc,l,mid,p);
else Insert(tr[u].rc,tr[v].rc,mid + 1,r,p);
}
int Merge(int u,int v) {
if(!u || !v) return u + v;
int res = ++Ncnt;
tr[res].lc = Merge(tr[u].lc,tr[v].lc);
tr[res].rc = Merge(tr[u].rc,tr[v].rc);
return res;
}
int que[MAXN * 2],c[MAXN];
struct sam {
struct node {
int par,len,nxt[26],cnt;
}tr[MAXN * 2];
int tail,root,last;
void Init() {
tail = 0;
root = last = ++tail;
memset(tr[tail].nxt,0,sizeof(tr[tail].nxt));
tr[tail].par = tr[tail].len = 0;
}
void build(int c) {
int nw = ++tail,p;
memset(tr[nw].nxt,0,sizeof(tr[nw].nxt));
tr[nw].len = tr[last].len + 1;tr[nw].cnt = 1;
for(p = last ; p && !tr[p].nxt[c] ; p = tr[p].par) {
tr[p].nxt[c] = nw;
}
if(!p) tr[nw].par = root;
else {
int q = tr[p].nxt[c];
if(tr[q].len == tr[p].len + 1) tr[nw].par = q;
else {
int cq = ++tail;
tr[cq] = tr[q];tr[cq].cnt = 0;
tr[cq].len = tr[p].len + 1;
tr[nw].par = tr[q].par = cq;
for(;p && tr[p].nxt[c] == q ; p = tr[p].par) {
tr[p].nxt[c] = cq;
}
}
}
last = nw;
}
void calc() {
for(int i = 1 ; i <= tail ; ++i) c[tr[i].len]++;
for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
for(int i = 1 ; i <= tail ; ++i) {
que[c[tr[i].len]--] = i;
}
for(int i = tail ; i >= 1 ; --i) {
int u = que[i];
if(tr[u].cnt) Insert(rt[u],rt[u],1,N,tr[u].len);
int f = tr[u].par;
rt[f] = Merge(rt[f],rt[u]);
}
}
}sam[2];
bool check(int p,int l,int r,int c) {
int t = Find(rt[p],1,N,l,r);
if(t == -1) return false;
return (t - l + 1) >= c;
}
void Solve() {
scanf("%s",s + 1);
N = strlen(s + 1);
read(Q);
sam[0].Init();
for(int i = 1 ; i <= N ; ++i) {
sam[0].build(s[i] - 'a');
}
sam[0].calc();
int l,r,len;
for(int i = 1 ; i <= Q ; ++i) {
scanf("%s",t + 1);
read(l);read(r);
len = strlen(t + 1);
sam[1].Init();
for(int j = 1 ; j <= len ; ++j) {
sam[1].build(t[j] - 'a');
int f = sam[1].tr[sam[1].last].par;
val[j] = sam[1].tr[f].len;
}
int p = sam[0].root,c = 0;
for(int j = 1 ; j <= len ; ++j) {
int h = t[j] - 'a';
while(p && !sam[0].tr[p].nxt[h]) {
p = sam[0].tr[p].par;
c = sam[0].tr[p].len;
}
if(!sam[0].tr[p].nxt[h]) {
c = 0;p = sam[0].root;
}
else {
p = sam[0].tr[p].nxt[h];++c;
while(!check(p,l,r,c)) {
--c;
int f = sam[0].tr[p].par;
if(c == sam[0].tr[f].len) p = f;
}
}
val[j] = max(val[j],c);
ans[i] += j - val[j];
}
}
for(int i = 1 ; i <= Q ; ++i) {
out(ans[i]);enter;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#else
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
#endif
Solve();
return 0;
}
【LOJ】#2720. 「NOI2018」你的名字的更多相关文章
- LOJ 2720 「NOI2018」你的名字——后缀自动机
题目:https://loj.ac/problem/2720 自己总是分不清 “SAM上一个点的 len[ ] ” 和 “一个串的前缀在 SAM 上匹配的 len ”. 于是原本想的 68 分做法是, ...
- loj#2720. 「NOI2018」你的名字
链接大合集: loj uoj luogu bzoj 单纯地纪念一下写的第一份5K代码.../躺尸 因为ZJOI都不会所以只好写NOI的题了... 总之字符串题肯定一上来就拼个大字符串跑后缀数组啦! ( ...
- LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增
题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...
- 「NOI2018」你的名字
「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...
- LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)
题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...
- loj#2718. 「NOI2018」归程
题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...
- loj#2721. 「NOI2018」屠龙勇士
题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...
- Loj #2719. 「NOI2018」冒泡排序
Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...
- loj 2719 「NOI2018」冒泡排序 - 组合数学
题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...
随机推荐
- (转)shell调试方法
---恢复内容开始--- 转载:https://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/ Shell脚本调试技术 曹 羽中2007 年 ...
- ssh sshpass随笔
1: 当通过ssh连接远程服务器的时候,可能会出现以下繁琐场景,需要手工输入yes: ssh username@ip 这对于某些分布式集群来说是不行的,甚至导致集群都不能启动成功,对于像pssh,ps ...
- vmware如何克隆多个linux系统
安装一次系统相对来说耗时较长,且还要做各种配置,那么克隆就不失为一种好的选择.接下来我把我做系统克隆的步骤写下来,供大家参考: 右键点击已经安装的虚拟机,选择管理-->克隆,接下来弹出一个窗口 ...
- 小程序 之修改radio默认样式
一.效果图 二.代码 /* 选中后的 背景样式 (红色背景 无边框 可根据UI需求自己修改) */ radio .wx-radio-input.wx-radio-input-checked { bor ...
- java集合类型源码解析之ArrayList
前言 作为一个老码农,不仅要谈架构.谈并发,也不能忘记最基础的语言和数据结构,因此特开辟这个系列的文章,争取每个月写1~2篇关于java基础知识的文章,以温故而知新. 如无特别之处,这个系列文章所使用 ...
- nessus在Linux上的安装
Nessus有三种安装方式 1.源文件安装 源文件安装是最复杂的安装方式,用此方式安装可以修改配置参数. 2.rpm安装 rpm安装比起源文件安装更简单一些,它已经把一些底层的东西写好了,用户只要按步 ...
- Java 面向对象(八)
常用类之String String字符串类 什么是字符串?就是把多个字符,串连一起. String字符串的本质 其实是一个char[]数组 /**该值用于字符存储.*/ private final c ...
- 阿里云 -- Go module 代理 镜像
简介 go module公共代理仓库,代理并缓存go模块.你可以利用该代理来避免DNS污染导致的模块拉取缓慢或失败的问题,加速你的构建 地址 https://mirrors.aliyun.com/go ...
- python操作excel实用脚本
import xlrd data = xlrd.open_workbook('/home/ppe/workspace/pythonwp/tianranqi_org.xls') table = data ...
- appium-FAQ(持续更新...)
Q1:未安装APP直接启用appium sever,初始化driver :driver = new AndroidDriver(new URL("http://127.0.0.1:4723/ ...