题解

把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」你的名字的更多相关文章

  1. LOJ 2720 「NOI2018」你的名字——后缀自动机

    题目:https://loj.ac/problem/2720 自己总是分不清 “SAM上一个点的 len[ ] ” 和 “一个串的前缀在 SAM 上匹配的 len ”. 于是原本想的 68 分做法是, ...

  2. loj#2720. 「NOI2018」你的名字

    链接大合集: loj uoj luogu bzoj 单纯地纪念一下写的第一份5K代码.../躺尸 因为ZJOI都不会所以只好写NOI的题了... 总之字符串题肯定一上来就拼个大字符串跑后缀数组啦! ( ...

  3. LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增

    题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...

  4. 「NOI2018」你的名字

    「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...

  5. LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)

    题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...

  6. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  7. loj#2721. 「NOI2018」屠龙勇士

    题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...

  8. Loj #2719. 「NOI2018」冒泡排序

    Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...

  9. loj 2719 「NOI2018」冒泡排序 - 组合数学

    题目传送门 传送门 题目大意 (相信大家都知道) 显然要考虑一个排列$p$合法的充要条件. 考虑这样一个构造$p$的过程.设排列$p^{-1}_{i}$满足$p_{p^{-1}_i} = i$. 初始 ...

随机推荐

  1. (转)代码审计利器-RIPS实践

    一.代码审计工具介绍 代码审计工具可以辅助我们进行白盒测试,大大提高漏洞分析和代码挖掘的效率. 在源代码的静态安全审计中,使用自动化工具辅助人工漏洞挖掘,一款好的代码审计软件,可以显著提高审计工作的效 ...

  2. ansible user模块

    查看模块的功能和选项,使用ansible-doc命令 ansible-doc options: -l #查看所有可用的模块 -m #查看模块的路径 -v #查看版本 -t TYPE #查看插件,插件: ...

  3. 数据结构Java版之排序算法(二)

    排序按时间复杂度和空间复杂度可分为 低级排序 和 高级排序 算法两种.下面将对排序算法进行讲解,以及样例的展示. 低级排序:冒泡排序.选择排序.插入排序. 冒泡排序: 核心思想,小的数往前移.假设最小 ...

  4. 实现一个简单的Tomcat

    实现一个简单的Tomcat 1. Tomcat作用 我们的web应用会运行在Tomcat中,那么显然请求必定是先到达Tomcat的,Tomcat对于请求实际上会进行如下的处理: 提供Socket服务: ...

  5. Linux fdisk命令创建扩展分区过程

    [root@localhost ~]# fdisk /dev/sdb …省略部分输出… Command (m for help): n #新建立分区 Command action e extended ...

  6. mysql 触发器语法详解

    1.创建Mysql触发器: 语法: CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW BE ...

  7. 阿里云 -- Go module 代理 镜像

    简介 go module公共代理仓库,代理并缓存go模块.你可以利用该代理来避免DNS污染导致的模块拉取缓慢或失败的问题,加速你的构建 地址 https://mirrors.aliyun.com/go ...

  8. vue-resource在vuecli3中请求headers修改

    this.$resource.delete({ user_code: Cookie.get("empid"), date: date, file_name: file_name } ...

  9. (十三)过滤器Filter(转)

    --------转自孤傲苍狼博客 一.Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例 ...

  10. (1) Java实现JDBC连接及事务的方式

    许多数据库的auto-commit默认是ON的,比如MySQL,PostgresSQL等.当然也有默认是OFF的,比如Oracle(Oracle里面执行DML语句是需要手动commit的). 这里我们 ...