[NOI2018] 你的名字
[NOI2018] 你的名字
Solution:
考虑一下 \(l=1,r=\left|S\right|\) 的时候怎么做,其实比较简单,我们对 \(S,T\) 都建立出 SAM,利用这个求得 \(p_i\),表示 \(T_{i-p_i+1,i}\) 在 \(S\) 上是一个连续子串,设 \(fir_i\) 表示 \(T\) 的 SAM 中,节点 \(i\) 代表的 \(endpos\) 中的最小值(事实上任意一个皆可),那么答案就是:
\[\sum_{i=2}^{tot} \max(0,len_i-\max(len_{fa_i},p_{fir_i}))
\]
拓展一下,发现 \(l,r\) 不表示整个 \(S\) 的时候,唯一有问题的就是 \(p_i\) 的求解,考虑求解 \(p_i\) 时,我们在走一条为 \(u\to v\) ,边权为 \(T_i\) 的边的时候,我们不能确定 \(v\) 的 \(endpos\) 中是否有元素在 \([l+len,r]\) 内(\(len\) 表示的是当前的匹配到的长度)。
那么不妨维护一下每个节点的等价类即可。
具体来说,我们在 parten tree 上考虑,每个节点的 \(endpos\) 就是他子节点的并集,且他若表示了一个前缀的话还有一个额外的元素(在构建 SAM 时可以处理)。
不妨用线段树合并来解决这个问题,每次就是询问 \(v\) 对应的线段树在某个区间上是否有值。
需要注意的是,我们正常求解 \(p_i\) 的时候,若遇到了无法从 \(u\) 继续走的情况是直接跳回到 \(u\) 在 parten tree 上的父亲的,但是这个地方我们每次只能将长度减去 \(1\),再次进行判断,直到必须跳回父亲。
Code:
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define p_b push_back
#define m_p make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define gcd __gcd
#define lowbit(x) (x&(-x))
using namespace std;
int rd(){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
return x*f;
}
void write(int x){
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=1e6+5,INF=0x3f3f3f3f;
char s[N];
int n,m;
struct seg{
struct node{
int l,r;
}tree[N*19];
int tot,root[N];
void change(int &k,int l,int r,int pos){
if(!k) k=++tot;
if(l==r) return;
if(pos<=mid)change(tree[k].l,l,mid,pos);
else change(tree[k].r,mid+1,r,pos);
}
void merge(int &k,int u,int v,int l,int r){
if(!u||!v) {k=u|v;return;}
k=++tot;
if(l==r) return;
merge(tree[k].l,tree[u].l,tree[v].l,l,mid);
merge(tree[k].r,tree[u].r,tree[v].r,mid+1,r);
}
bool ask(int k,int l,int r,int x,int y){
if(!k||x>y) return 0;
if(x<=l&&r<=y) return 1;
if(x<=mid&&ask(tree[k].l,l,mid,x,y)) return 1;
if(y>mid&&ask(tree[k].r,mid+1,r,x,y))return 1;
return 0;
}
}T;
struct SAM{
struct node{
int fa,nxt[26],len,fir;
}a[N];
int pos[N],p[N],tot=1,last=1;ll ans=0;
vector<int> G[N];
inline void insert(int ch,int id){
int cur=++tot,p=last;
a[cur].len=a[last].len+1,a[cur].fir=id;
for(;p&&!a[p].nxt[ch];p=a[p].fa) a[p].nxt[ch]=cur;
int q=a[p].nxt[ch];
if(!q) a[cur].fa=1;
else if(a[q].len==a[p].len+1) a[cur].fa=q;
else{
int r=++tot;
a[r]=a[q],a[r].len=a[p].len+1;
for(;p&&a[p].nxt[ch]==q;p=a[p].fa) a[p].nxt[ch]=r;
a[q].fa=a[cur].fa=r;
}
last=cur,pos[cur]=id;
}
void dfs(int u){
if(pos[u]) T.change(T.root[u],1,n,pos[u]);
for(auto v : G[u]) dfs(v),T.merge(T.root[u],T.root[u],T.root[v],1,n);
}
inline void build(){
for(int i=2;i<=tot;i++) G[a[i].fa].p_b(i);
dfs(1);
}
inline ll ask(){
for(int i=2;i<=tot;i++) ans+=max(0,a[i].len-max(a[a[i].fa].len,p[a[i].fir]));
return ans;
}
inline void init(){
for(int i=1;i<=tot;i++){
memset(a[i].nxt,0,sizeof(a[i].nxt));
pos[i]=p[i]=0;
}
tot=1,last=1,ans=0;
}
}s1,s2;
void solve(){
scanf("%s",s+1);m=strlen(s+1);
s2.init();
for(int i=1;i<=m;i++) s2.insert(s[i]-'a',i);
int l=rd(),r=rd(),now=1,len=0,ch;
for(int i=1;i<=m;i++){
ch=s[i]-'a';
while(1){
if(s1.a[now].nxt[ch]&&T.ask(T.root[s1.a[now].nxt[ch]],1,n,l+len,r)){
++len,now=s1.a[now].nxt[ch];
break;
}
if(!len) break;
--len;
if(len==s1.a[s1.a[now].fa].len) now=s1.a[now].fa;
}
s2.p[i]=len;
}
printf("%lld\n",s2.ask());
}
int main(){
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++) s1.insert(s[i]-'a',i);
s1.build();
int q=rd();
while(q--)solve();
return 0;
}

浙公网安备 33010602011771号