【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$. 初始 ...
随机推荐
- JSP+Oracle实现分页功能
Oracle: create table load( id char(200) not null, title varchar2(100) not null, time varchar2(100) n ...
- selenium鼠标操作
#-*- coding:utf-8 -*- import time from selenium import webdriver from selenium.webdriver.common.acti ...
- Java基础系列 - 抽象类,子类继承
package com.company; /** * 抽象类继承 * 用abstract修饰类就是抽象类 * 用abstract修饰方法就是抽象方法(一般使用比较少) * 抽象类不能被实例化 */ p ...
- zookeeper 随记
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务. zookeeper的几种模式: 1.单点模式 2.分布式集群模式,节点运行在多台机器 3.单点多实例 在这里只介绍单点多实例安装. ...
- Linux 如何通过某一台服务器调用执行多台远程服务器上的脚本,结果显示在本地?
现在都流行自动化运维了,可能目前技术不够,很多自动化工具还不怎么会用,所以本次只是通过ssh来实现功能. 说明:自己写的一个简单脚本,只是实现了基础功能,还有待优化. 一共三台机器: master:1 ...
- EXTJS框架-入门实例
extjs框架是一个JavaScript框架,可以渲染出丰富的控件 实例: 代码: <html> <head> <title>test</title> ...
- python 3 安装
如果本机安装了python2,尽量不要管他,使用python3运行python脚本就好,因为可能有程序依赖目前的python2环境, 比如yum!!!!! 不要动现有的python2环境! 一.安装p ...
- Java实体类为什么要实现序列化
public class User implements Serializable {} 客户端访问了某个能开启会话功能的资源, web服务器就会创建一个与该客户端对应的HttpSession对象,每 ...
- C#图片灰度处理(位深度24→位深度8)、C#图片二值化处理(位深度8→位深度1)
C#图片灰度处理(位深度24→位深度8) #region 灰度处理 /// <summary> /// 将源图像灰度化,并转化为8位灰度图像. /// </summary> / ...
- ngx.shared.DICT.incr 详解
ngx.shared.DICT.incr 原文: ngx.shared.DICT.incr syntax: newval, err, forcible? = ngx.shared.DICT:incr( ...