【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$. 初始 ...
随机推荐
- CSPS模拟86-87
模拟86 T1,烧水,按位统计贡献,利用某种sao操作避免数位dp #include<iostream> #include<cstdio> #include<cstrin ...
- Semaphore信号量原理
package com.maven.info.semaphore; import java.util.ArrayList; import java.util.List; import java.uti ...
- ZR#988
ZR#988 解法: 先算出横着能排多少座位, 以及需要排几列, 才能把 n 个座位全部排下来.要使得尽量多的位置在走廊边上, 于是在 n 列中插入走廊的策略是显然的, 我们只要以两列为单位, 在其中 ...
- linux redhat 安装了jdk检查版本不是自己安装的版本的解决办法
Linux下安装jdk java -version 不是自己所需要的版本 设置环境变量,这是最重要的 在etc/profile文件下添加 export JAVA_HOME=/usr/java/jdk1 ...
- LeetCode 第 151 场周赛
一.查询无效交易(LeetCode-1169) 1.1 题目描述 1.2 解题思路 根据,它和另一个城市中同名的另一笔交易相隔不超过 60 分钟(包含 60 分钟整) 得出 城市A和其他城市任何一笔交 ...
- SpringCloud学习整理
参考文档 [1]: Spring Cloud Ribbon负载均衡
- case设计及验证:入口+页面+展示
测试个性CB问题, 功能整体结构为:入口+页面+展示 总结: 1. 产品文档为主,其次是服务端接口返回.数据结构及字段值确认.结合实际场景检查是否有遗漏或不合理. 2. 以字段为维度,每个字段的检查点 ...
- https://www.atlassian.com/git/tutorials/git-gc
https://www.atlassian.com/git/tutorials/git-gc The git gc command is a repository maintenance comman ...
- vue——vuex安装及使用
一.vuex 的理解 官方解释:vuex是一个专为vue.js应用程序开发的状态管理模式.采用集中式存储管理应用的所有的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 我的理解:全局变量,同 ...
- MySQL中表的复制以及大型数据表的备份教程
MySQL中表的复制以及大型数据表的备份教程 这篇文章主要介绍了MySQL中表的复制以及大型数据表的备份教程,其中大表备份是采用添加触发器增量备份的方法,需要的朋友可以参考下 表复制 mysq ...