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

题意是:每次给定一个串\(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. 新电脑重新安装win10+python3.6+anaconda+tensorflow1.12(gpu版)

    安装了一天的软件,遇到了很多坑,在快泪崩的时候,始终以磨刀不误砍柴工鼓励自己,坚持安好了,话不多说,上干货: 前言:        TensorFlow 有两个版本:CPU 版本和 GPU 版本.GP ...

  2. OpenFlow1.3协议wireshark抓包分析

    OpenFlow v1.0 v1.0协议消息列表如下: 分为三类消息:Controller-to-switch,asynchronous和symmertric. v1.0(包含至少一个流表,每个流表包 ...

  3. 利用JS验证查询参数-选择月份后必须选择年份

    js代码: function queryAgentInfo(){ // 标记 var flag=false; //遍历所有月份 $(".month").each(function( ...

  4. Python构造方法、析构方法和单例模式

    一.__init__()方法 __init__()通常在初始化一个类实例的时候调用,如: class Student(object): def __init__(self,name,age): sel ...

  5. 查看sql语句加锁信息

    问题: 最近使用quartz集群,总是报deadlock问题,所以需要查看一下执行的sql导致的加锁冲突. 步骤: 1.在要测试的库中创建指定表innodb_lock_monitor create t ...

  6. C# byte[] 转换16进制字符串

    1.byte[] 转换16进制字符串 1.1 BitConverter方式 var str = DateTime.Now.ToString(); var encode = Encoding.UTF8; ...

  7. 洛谷P2746校园网

    传送门啦 下面来看任务B.我们发现,图中只要存在入度为0的点和出度为0的点就永远不可能满足要求:" 不论我们给哪个学校发送新软件,它都会到达其余所有的学校 ".我们还发现,只要在入 ...

  8. PHP实现菜单无限极分类

    菜单数据 这里我们的菜单数据是临时数据, 没有从数据库中查询处理,数据基本和数据库中的的相似. 数据如下: $items = array( 1 => array('id' => 1, 'p ...

  9. 使用mui框架打开页面的几种不同方式

    1.创建子页面: list.html就是index.html的子页面,创建代码比较简单,如下: mui.init({ subpages: [{ url: 'list.html', //子页面HTML地 ...

  10. Kubernetes 部署kafka ACL(单机版)

    一.概述 在Kafka0.9版本之前,Kafka集群时没有安全机制的.Kafka Client应用可以通过连接Zookeeper地址,例如zk1:2181:zk2:2181,zk3:2181等.来获取 ...