Pty loves string 树上主席树,border,kmp Fail树
Posted 爷灬傲奈我何123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pty loves string 树上主席树,border,kmp Fail树相关的知识,希望对你有一定的参考价值。
link
题意:
给定一个字符串,Q次询问,每次询问一个x,y,代表前后缀拼起来的字符串,询问该字符串在原串中的可重叠匹配次数。
Q<=2e5 n<=2e5
思路:
由于题目给的是[1,x] [n-y+1,n] ,如果匹配的话,那么就是匹配了[l,l+x-1],[r-y+1,r],且l+x==r-y+1,我们前后缀分开考虑,这里仅讨论前缀,对于某个前缀k,考虑在kmp的过程中,它一直通过fail指针暴跳,这里来复习一下fail指针,对于红色指针他他所对应的fail就是黑色指针,那么对于
a
b
a
b
abab
abab这个串来说红色是能满足性质的,那么对于
a
b
ab
ab这个串来说亦是如此。所以红色节点都在它们的子树中。
fail指针对应的下标就是对于以i结尾的字符串的最大相同前后缀,对于路径上的任意个点,他都是能满足[l,l+x-1]这一性质,那么就启发我们建图考虑,对于某个节点,他的子树中的每一个点都是能满足的。那么后缀也是如此,只需要倒着来维护。现在问题就变成了给你两棵树,并且两个id的子树,求两个子树中相同的节点的个数,现在我们已经建好了第一个树,那么主席树上的节点建立的是什么呢?主席树的下标是按照该树的dfs序建立的,他的节点就是对应的满足u+1的在第二个树上的dfs序,这样我们在查询的时候,只需要查询某个子树的主席树,并且dfs序在第二个子树r的范围内的所有对应节点就行了。复杂度
O
(
l
o
g
n
)
.
O(log n).
O(logn).代码:
//#pragma GCC target("avx")
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast")
// created by myq
#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
#define x first
#define y second
typedef pair<int,int> pii;
const int N = 200010;
const int mod=998244353;
struct node{
int l;
int r;
int sum;
}tr[N*40];
int ne[N];
int ne2[N];
int idx;
int dfn2[N];
int dfn[N];
int w[N];
int w2[N];
int cnt;
int cnt2;
vector<int>v[N],g[N];
int sz[N],sz2[N];
int root[N];
int n;
void insert(int p,int &q,int x,int l,int r)
{
q=++idx;
tr[q].sum=0;
tr[q]=tr[p];
if(x<l || x>r) return ;
if(l==r)
{
tr[q].sum++;
return ;
}
int mid=l+r>>1;
if(x<=mid)
insert(tr[p].l,tr[q].l,x,l,mid);
else
insert(tr[p].r,tr[q].r,x,mid+1,r);
tr[q].sum=tr[tr[q].l].sum+tr[tr[q].r].sum;
}
int query(int p,int q,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
return tr[q].sum-tr[p].sum;
int mid=l+r>>1;
int res=0;
if(ql<=mid) res+=query(tr[p].l,tr[q].l,l,mid,ql,qr);
if(qr>mid) res+=query(tr[p].r,tr[q].r,mid+1,r,ql,qr);
return res;
}
void dfs1(int u,int fa)
{
if(u)
{
dfn[u]=++cnt;
w[cnt]=u;
}
sz[u]=1;
for(auto j:v[u])
{
dfs1(j,u);
sz[u]+=sz[j];
}
}
void dfs2(int u)
{
// cout<<u<<"->";
if(u)
dfn2[u]=++cnt2;
sz2[u]=1;
for(auto j:g[u])
{
dfs2(j);
sz2[u]+=sz2[j];
}
}
char s[N];
void solve()
{
int q;
scanf("%d%d",&n,&q);
scanf("%s",s+1);
dfn2[n+1]=0;
for(int i=1;i<=n;i++) ne2[i]=n+1,ne[i]=0;
for(int i=0;i<=n;i++) v[i].clear(),g[i].clear();
int j=0;
for(int i=2;i<=n;i++)
{
while(j&&s[j+1]!=s[i]) j=ne[j];
if(s[j+1]==s[i]) j++;
ne[i] =j;
}
j=n+1;
for(int i=n-1;i>=1;i--)
{
while(j!=n+1&&s[j-1]!=s[i]) j=ne2[j];
if(s[j-1]==s[i]) j--;
ne2[i]=j;
}
for(int i=1;i<=n;i++)
{
v[ne[i]].push_back(i);
// cout<<ne[i]<<" "<<i<<endl;
if(ne2[i]==n+1)
g[0].push_back(i);
else
{
// cout<<ne2[i]<<" "<<i<<endl;
g[ne2[i]].push_back(i);
}
}
dfs2(0);
dfs1(0,-1);
for(int i=1;i<=n;i++)
{
insert(root[i-1],root[i],dfn2[w[i]+1],1,n);
}
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
if(l+r>n)
{
puts("0");
continue;
}
r=n-r+1;
printf("%d\\n",query(root[dfn[l]-1],root[dfn[l]+sz[l]-1],1,n,dfn2[r],dfn2[r]+sz2[r]-1));
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
idx=0;
cnt=0;
cnt2=0;
memset(tr,0,sizeof tr);
solve();
}
return 0;
}
/**
* In every life we have some trouble
* When you worry you make it double
* Don't worry,be happy.
**/
以上是关于Pty loves string 树上主席树,border,kmp Fail树的主要内容,如果未能解决你的问题,请参考以下文章
2021牛客暑期多校训练营7 F.xay loves trees(主席树+树上尺取)
2021牛客暑期多校训练营7 F.xay loves trees 主席树+dfs序
牛客多校7.F.xay loves trees 主席树+dfs序
2021牛客多校7 - xay loves trees(dfs序+主席树-标记永久化)