其实很水的一道题吧....

题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串


统计\(T\)本质不同的串,建个后缀自动机

然后自然的可以想到,对于每个\(T\)的子串,它对应了一个\(right\)集合

那么,它应该会被这个\(right\)集合所限制

考虑对于每个\(i\),求出最小的\(l\)使得\(T[l .. i]\)存在于\(S[l..r]\)中

这个可以套个线段树转移

然后就没了.....


如果不需要统计\(T\)本质不同的串,又怎么做呢?

统计的时候乘上\(right\)集合大小就行


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; #define ri register int
#define ll long long
#define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
#define drep(io, ed, st) for(ri io = ed; io >= st; io --) #define gc getchar
inline int read() {
int p = 0, w = 1; char c = gc();
while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
return p * w;
} const int sid = 1005000;
const int eid = 30000000 + 5; struct SAM { int id, fa[sid], mx[sid];
int go[sid][26], mc[sid]; inline int newnode() {
++ id;
fa[id] = mx[id] = mc[id] = 0;
memset(go[id], 0, sizeof(go[id]));
return id;
} inline void init() {
id = 0;
newnode();
} inline int extend(int lst, int c, int pos) {
int np = newnode(), p = lst;
mx[np] = mx[p] + 1; mc[np] = pos;
for( ; p && !go[p][c]; p = fa[p])
go[p][c] = np;
if(!p) fa[np] = 1;
else {
int q = go[p][c];
if(mx[p] + 1 == mx[q]) fa[np] = q;
else {
int nq = newnode(); mx[nq] = mx[p] + 1;
fa[nq] = fa[q]; fa[np] = fa[q] = nq;
memcpy(go[nq], go[q], sizeof(go[q]));
for( ; p && go[p][c] == q; p = fa[p])
go[p][c] = nq;
}
}
return np;
} } S, T; int q, n, m, seg;
char s[sid], t[sid];
int nc[sid], ip[sid], w[sid], val[sid];
int rt[sid], ls[eid], rs[eid]; inline int merge(int x, int y) {
if(!x || !y) return x + y;
int o = ++ seg;
ls[o] = merge(ls[x], ls[y]);
rs[o] = merge(rs[x], rs[y]);
return o;
} inline void ins(int &o, int l, int r, int p) {
o = ++ seg;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) ins(ls[o], l, mid, p);
else ins(rs[o], mid + 1, r, p);
} inline bool qry(int o, int l, int r, int ml, int mr) {
if(ml > r || mr < l || ml > mr || !o) return 0;
if(ml <= l && mr >= r) return 1;
int mid = (l + r) >> 1;
if(qry(ls[o], l, mid, ml, mr)) return 1;
else return qry(rs[o], mid + 1, r, ml, mr);
} inline void init() {
S.init();
int lst = 1;
rep(i, 1, n) lst = S.extend(lst, s[i] - 'a', i);
int id = S.id;
rep(i, 1, id) nc[S.mx[i]] ++;
rep(i, 1, n) nc[i] += nc[i - 1];
rep(i, 1, id) ip[nc[S.mx[i]] --] = i;
rep(i, 1, id)
if(S.mc[i])
ins(rt[i], 1, n, S.mc[i]);
drep(i, id, 1) {
int o = ip[i], f = S.fa[o];
rt[f] = merge(rt[f], rt[o]);
}
} void Match(int l, int r) {
int o = 1, nl = 0;
rep(i, 1, m) {
int c = t[i] - 'a';
while(1)
{
int nxt = S.go[o][c], f = S.fa[o];
if(nxt && qry(rt[nxt], 1, n, l + nl, r))
{
nl ++; o = nxt;
break;
}
if(!nl) break; nl --;
if(nl == S.mx[f]) o = f;
}
w[i] = nl;
}
} int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
init(); q = read();
rep(i, 1, q) { scanf("%s", t + 1);
m = strlen(t + 1); T.init();
int lst = 1;
rep(j, 1, m) lst = T.extend(lst, t[j] - 'a', j); int l = read(), r = read();
Match(l, r); int id = T.id;
rep(i, 1, id) nc[i] = val[i] = 0;
rep(i, 1, id) nc[T.mx[i]] ++;
rep(i, 1, id) nc[i] += nc[i - 1];
rep(i, 1, id) ip[nc[T.mx[i]] --] = i;
drep(i, id, 1) {
int o = ip[i], f = T.fa[o];
if(T.mc[o]) val[o] = w[T.mc[o]];
val[f] = max(val[f], val[o]);
} ll ans = 0;
rep(i, 1, id) ans += max(T.mx[i] - max(T.mx[T.fa[i]], val[i]), 0);
printf("%lld\n", ans);
}
return 0;
}

luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并的更多相关文章

  1. bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)

    bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...

  2. BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并

    题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...

  3. BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)

    LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...

  4. NOI 2018 你的名字 (后缀自动机+线段树合并)

    题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...

  5. [NOI2018]你的名字(后缀自动机+线段树)

    题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...

  6. BZOJ3413: 匹配(后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...

  7. cf666E. Forensic Examination(广义后缀自动机 线段树合并)

    题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...

  8. [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)

    https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...

  9. 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)

    模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...

随机推荐

  1. mnist 手写数字识别

    mnist 手写数字识别三大步骤 1.定义分类模型2.训练模型3.评价模型 import tensorflow as tfimport input_datamnist = input_data.rea ...

  2. js数组排序 reverse()和sort()方法的使用

    WEB前端|js数组排序reverse()和sort()方法的使用,数组中已经存在两个可以直接用来重排序的方法:reverse()和sort(). reverse()方法会对反转数组项的顺序. var ...

  3. vue中使用localStorage存储信息

    一 什么是localStorage 对浏览器来说,使用 Web Storage 存储键值对比存储 Cookie 方式更直观,而且容量更大,它包含两种:localStorage 和 sessionSto ...

  4. phpStudy apache无法启动 apache启动后又停止

    一.是防火墙拦截: 二.是80端口已经被别的程序占用,如IIS,迅雷等: 三.是没有安装VC9运行库,php和apache都是VC9编译: 四.虚拟机配置路径中有中文: 五.在检测端口后强制重启 把配 ...

  5. Intent 对象在 Android 开发中的应用

    转自(http://www.ibm.com/developerworks/cn/opensource/os-cn-android-intent/) Android 是一个开放性移动开发平台,运行在该平 ...

  6. Vagrant 无法校验手动下载的 Homestead Box 版本

    起因 4年前电脑,配置不太好了,现有的 Homestead 运行起来太吃内存.在修改了 Homestead.yaml 文件里 memory 选项的内存配置为 1024 后,应用最新配置重启失败. 索性 ...

  7. strcpy unsigned char

    http://bbs.csdn.net/topics/250068243 char *strcpy(char* dest, const char *src); 用unsigned char编译会出错 ...

  8. yum命令安装软件时,出现--centos 7 安装apache 出现 Could not resolve host: mirrorlist.centos.org; 未知的错误"--CentOS网络设置 couldn't resolve host 'mirrorlist.centos.org问题解决

    CentOS网络设置 couldn't resolve host 'mirrorlist.centos.org问题解决 今天在虚拟机上安装完CentOS6.5之后,首次使用时yum命令安装软件时,出现 ...

  9. 关于主键的设计、primary key

    主键:用于唯一标识一个表中一行数据. 外键:用于建立两个表之间的关系,A表中有一列是B表中的主键,那么A表中这列的数据就受到B表主键的约束. 那么关于主键应该如何设计呢,这里我说下优缺点: 1.用自动 ...

  10. IntelliJ IDEA 里 查看一个函数注释的方法是 ctrl+q

     ctrl + q 也可以看到 官方的文档注释,java真是个强大的东西,官方的每个函数都有注释,这些注释 自动生成了官方的文档,所以看官方的注释 就是 看 官方的文档.