bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ 有多少本质不同的子串不是 $ S[l,r] $ 的子串。
题解时间
跟上一道题有点像哈。
只不过这一次是要将 $ T $ 放在 $ S $ 上匹配。
我们先不管每次选取的 $ S $ 段不同,就假设我们已经建好了选取的 $ S $ 段的SAM(也就是前68pts啦)
我们直接把 $ T $ 放上去匹配,维护现在匹配上的最长长度 $ lnow $ ,当 $ px $ 跳到 $ tranc $ 时 $ lnow++ $ ,
当失配时 $ px $ 直接跳到 $ pre $ , $ lnow $ 修改为对应点的 $ len $ 。
但很明显不能每次重新建一遍 $ S $ 的SAM。
所以考虑让上面的匹配操作可以查询在 $ [l,r] $ 区间内某点有无 $ endpos $ 。
写个线段树合并就好。
然后此时上面匹配操作的“失配”步骤需要改一下。
正常是跳到 $ pre $ 后直接修改 $ lnow $ ,但这里不行。
因为:假设我们匹配到某点 $ x $ ,字符串 $ T $ 上 $ ch $ , $ lnow $ 已知,
我们要查询的就是 $ tranc[x][ch] $ 的 $ [l+lnow,r] $ 区间。
所以失配时改成 $ lnow-- $ ,直到 $ lnow==len[pre[x]] $ 时才跳 $ pre $ 。
很明显这样匹配依然是 $ O(nlogn) $ ,那个log来自线段树。
然后就完事了。
设 $ T $ 上匹配第 $ i $ 个字符后的 $ lnow=ma[i] $ ,
则 $ ans=\Sigma (T的SAM上每一个节点) len[x]-max(len[pre[x]],ma[ip[x]]) $
( $ ip[x] $ 指TSAM上 $ x $ 点对应 $ T $ 的哪一位字符)
丑陋的封装-1000
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
namespace RKK
{
const int N=2000011;
int TAT;
char s0[N],s1[N];int l0,l1;
int rt[N],tcnt,lson[N*44],rson[N*44];
struct sumireko{int to,ne;}e[N];int he[N],ecnt;
void addline(int f,int t){e[++ecnt].to=t;e[ecnt].ne=he[f],he[f]=ecnt;}
void insert(int x,int &px,int pl,int pr)
{
if(!px) px=++tcnt;
if(pl==pr) return;
int pm=pl+pr>>1;
if(x<=pm) insert(x,lson[px],pl,pm);
else insert(x,rson[px],pm+1,pr);
}
int merge(int px,int py,int pl,int pr)
{
if(!px||!py) return px|py;
int pz=++tcnt;
if(pl!=pr)
{
int pm=pl+pr>>1;
lson[pz]=merge(lson[px],lson[py],pl,pm);
rson[pz]=merge(rson[px],rson[py],pm+1,pr);
}
return pz;
}
int query(int l,int r,int px,int pl,int pr)
{
if(l>r||!px) return 0;
if(l<=pl&&r>=pr) return 1;
int pm=pl+pr>>1;
int ret=0;
if(l<=pm) ret|=query(l,r,lson[px],pl,pm);
if(r>pm) ret|=query(l,r,rson[px],pm+1,pr);
return ret;
}
struct remilia{int tranc[26],len,pre;};
int ma[N];
struct sakuya
{
remilia s[N];
int ip[N];
int fin,size;
sakuya(){fin=size=1;}
void set(){memset(s,0,sizeof(remilia)*(size+10)),memset(ip,0,sizeof(int)*(size+10)),fin=size=1;}
void ins(int ch,int i=0)
{
int npx,npy,lpx,lpy;
npx=++size;
s[npx].len=s[fin].len+1;ip[npx]=i;
for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx;
if(!lpx) s[npx].pre=1;
else
{
lpy=s[lpx].tranc[ch];
if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy;
else
{
npy=++size;
s[npy]=s[lpy],ip[npy]=ip[lpy];
s[npy].len=s[lpx].len+1;
s[npx].pre=s[lpy].pre=npy;
while(s[lpx].tranc[ch]==lpy)
{
s[lpx].tranc[ch]=npy;
lpx=s[lpx].pre;
}
}
}
fin=npx;
}
void dfs(int x)
{
for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to) dfs(t),rt[x]=merge(rt[x],rt[t],1,l0);
}
void work()
{
for(int i=2;i<=size;i++) addline(s[i].pre,i);
dfs(1);
}
}sam,sam1;
void work(int l,int r)
{
sam1.set();
for(int i=1;i<=l1;i++) sam1.ins(s1[i]-'a',i);
int px=1,lnow=0;
for(int i=1;i<=l1;i++)
{
while(px!=1&&!query(l+lnow,r,rt[sam.s[px].tranc[s1[i]-'a']],1,l0))
{
lnow--;
if(lnow==sam.s[sam.s[px].pre].len) px=sam.s[px].pre;
}
if(query(l+lnow,r,rt[sam.s[px].tranc[s1[i]-'a']],1,l0))
{
lnow++;
px=sam.s[px].tranc[s1[i]-'a'];
}
ma[i]=lnow;
}
lint ans=0;
for(int i=2;i<=sam1.size;i++) ans+=max(0,sam1.s[i].len-max(sam1.s[sam1.s[i].pre].len,ma[sam1.ip[i]]));
printf("%lld\n",ans);
memset(ma,0,sizeof(int)*(l1+5));
}
int Iris()
{
scanf("%s",s0+1),l0=strlen(s0+1);
for(int i=1;i<=l0;i++) sam.ins(s0[i]-'a'),insert(i,rt[sam.fin],1,l0);
sam.work();
scanf("%d",&TAT);
for(int rkk=1,l,r;rkk<=TAT;rkk++)
{
scanf("%s%d%d",s1+1,&l,&r),l1=strlen(s1+1);
work(l,r);
}
return 0;
}
}
int main(){return RKK::Iris();}
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)的更多相关文章
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- BZOJ.5417.[NOI2018]你的名字(后缀自动机 线段树合并)
LOJ 洛谷 BZOJ 考虑\(l=1,r=|S|\)的情况: 对\(S\)串建SAM,\(T\)在上面匹配,可以得到每个位置\(i\)的后缀的最长匹配长度\(mx[i]\). 因为要去重,对\(T\ ...
- luogu4770 [NOI2018]你的名字 后缀自动机 + 线段树合并
其实很水的一道题吧.... 题意是:每次给定一个串\(T\)以及\(l, r\),询问有多少个字符串\(s\)满足,\(s\)是\(T\)的子串,但不是\(S[l .. r]\)的子串 统计\(T\) ...
- NOI 2018 你的名字 (后缀自动机+线段树合并)
题目大意:略 令$ION2017=S,ION2018=T$ 对$S$建$SAM$,每次都把$T$放进去跑,求出结尾是i的前缀串,能匹配上$S$的最长后缀长度为$f_{i}$ 由于$T$必须在$[l,r ...
- [NOI2018]你的名字(后缀自动机+线段树)
题目描述 小A 被选为了ION2018 的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了. 由于ION 已经举办了很多届,所以在题目命名上也是有规定的,ION 命题手 ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
随机推荐
- Solution -「NOI.AC 省选膜你赛」寄蒜几盒
题目 题意简述 给定一个含有 \(n\) 个顶点的凸多边形( \(n\) 是偶数),对于每一对相对的边(即中间有 \(\frac{n}2-1\) 条其它边),延长它们以将平面分割为多块,并把包含原 ...
- Solution -「NOI 模拟赛」彩色挂饰
\(\mathcal{Description}\) 给定一个含 \(n\) 个点 \(m\) 条边的简单无向图,设图中最大点双的大小为 \(s\),则保证 \(s\le6\).你将要用 \(k\) ...
- 通过修改注册表将右alt键映射为application键
通过修改注册表将右alt键映射为application键的方法有许多键盘没有APPLICATION(上下文菜单)键,本文将教您如何把右ALT键映射为apps键.1.映射请将以下注册表信息用记事本保存为 ...
- 一个接口多个实现Spring如何实现正确注入
阅读源码的时候,时不时脑子中冒出一个问题:如果自己写一个单例的容器,一个接口多个实现怎么正确地将每个实现都缓存容器中并准确地注入呢?阅读Dubbo源码时也有类似的问题冒出来. 对于@Autowire注 ...
- 聊聊MySQL的加锁规则《死磕MySQL系列 十五》
大家好,我是咔咔 不期速成,日拱一卒 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升MySQ ...
- Nginx服务器SSL证书安装
操作场景 本文档指导您如何在 Nginx 服务器中安装 SSL 证书. 说明: 本文档以证书名称 www.domain.com 为例. Nginx 版本以 nginx/1.16.0 为例. 当前服务器 ...
- Java IO 技术
文章目录 流的概念 IO 流类体系 InputStream / OutputStream Reader / Writer 文件字节流 文件字符流 缓冲字节流 缓冲字符流 字节数组流 数据流 转换流 序 ...
- 传输层隧道技术之lcx内网端口转发
传输层技术包括TCP隧道.UDP隧道和常规端口转发等.在渗透测试中,如果内网防火墙阻止了指定端口的访问,在获得目标机器的权限后,可以使用IPTABLES打开指定端口.如果内网中存在一系列防御系统,TC ...
- C#CancellationToken/CancellationTokenSource-取消令牌/取消令牌源 CT/CTS
详细情况:https://www.cnblogs.com/wucy/p/15128365.html 背景 为什么引入取消令牌? Thread.abort()方法会破坏同步锁中代码的原子逻辑,破坏锁的作 ...
- Codeforces Round #726 (Div.2) A-E1 题解
A. Arithmetic Array 题目大意:一串数,求添加多少个非负整数后平均值为1 代码: //CF726A #include<bits/stdc++.h> using names ...