专项测试 字符串1
A.回文子串
我是憨批,调了一年的分块做法,死活过不了拍。写了9k,还被D了。
设 \(d_i\) 表示以 \(i\) 为起点的回文子串的数量 (长度小于 \(k\) )
这个可以直接哈希暴力搞
那么答案就是 \(\sum\limits_{i=l}^{r-K+1}d_i\) 再加上 \([r-k+2,r]\) 区间内的回文子串数量
再看修改,是将一段区间内的字符都变成相同的,那么 \([l,r-k+1]\) 这一段都会变成 \(k\)
会被影响到的有 \([l-k+1,l)\) 和 \([r-k+2,r]\)
由于 \(k\) 很小所以可以每次把小区间内的暴力计算,大的区间可以使用线段树来维护
对于字符的维护也可以用线段树这样可以做到 \(O(\log)-O(\log)\) 也可以分块 \(O(\sqrt{n})-O(1)\)
Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define uint unsigned long long
#define lson rt<<1
#define rson rt<<1|1
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,K,q;
int d[50010];
uint HASH[50010],iHASH[50010],pw[50010];
char s[50010],sss[3];
inline uint getHASH(int l,int r){return HASH[r]-HASH[l-1]*pw[r-l+1];}
inline uint getiHASH(int l,int r){return iHASH[l]-iHASH[r+1]*pw[r-l+1];}
namespace Segment1{
struct Seg1{int sum,atag;}st[50010*4];
inline void pushup(int rt){st[rt].sum=st[lson].sum+st[rson].sum;}
inline void pushdown(int rt,int l,int r){
if(st[rt].atag){
int mid=(l+r)>>1;
st[lson].sum=st[rt].atag*(mid-l+1);
st[rson].sum=st[rt].atag*(r-mid);
st[lson].atag=st[rt].atag;
st[rson].atag=st[rt].atag;
st[rt].atag=0;
}
}
void build(int rt,int l,int r){
if(l==r) return st[rt].sum=d[l],void();
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void upd(int rt,int l,int r,int L,int R,int k){
if(L<=l&&r<=R) return st[rt].sum=k*(r-l+1),st[rt].atag=k,void();
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(L<=mid) upd(lson,l,mid,L,R,k);
if(R>mid) upd(rson,mid+1,r,L,R,k);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R) return st[rt].sum;
pushdown(rt,l,r);
int mid=(l+r)>>1,res=0;
if(L<=mid) res+=query(lson,l,mid,L,R);
if(R>mid) res+=query(rson,mid+1,r,L,R);
return res;
}
void print(int rt,int l,int r){
printf("l : %lld r : %lld sum : %lld atag : %lld\n",l,r,st[rt].sum,st[rt].atag);
if(l==r) return ;
int mid=(l+r)>>1;
print(lson,l,mid);
print(rson,mid+1,r);
}
}
namespace Segment2{
struct Seg2{char c,atag;}st[50010*4];
inline void pushdown(int rt){
if(st[rt].atag>='a'&&st[rt].atag<='z'){
st[lson].atag=st[rt].atag;
st[rson].atag=st[rt].atag;
st[rt].atag='~';
}
}
void build(int rt,int l,int r){
if(l==r) return st[rt].c=s[l],void();
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
}
void upd(int rt,int l,int r,int L,int R,char k){
if(L<=l&&r<=R) return st[rt].atag=k,void();
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid) upd(lson,l,mid,L,R,k);
if(R>mid) upd(rson,mid+1,r,L,R,k);
}
char query(int rt,int l,int r,int pos){
if(st[rt].atag>='a'&&st[rt].atag<='z') return st[rt].atag;
if(l==r) return st[rt].c;
int mid=(l+r)>>1;
if(pos<=mid) return query(lson,l,mid,pos);
else return query(rson,mid+1,r,pos);
}
}
inline int getANS(int l,int r){
int p=0,res=0;
for(int i=l;i<=r;i++) s[++p]=Segment2::query(1,1,n,i);
for(int i=1;i<=p;i++) HASH[i]=HASH[i-1]*131+s[i]-'a';
iHASH[p+1]=0;for(int i=p;i;i--) iHASH[i]=iHASH[i+1]*131+s[i]-'a';
for(int i=1;i<=p;i++) d[i]=0;
for(int i=1;i<=p;i++) for(int j=1;j<=K;j++) if(i+j-1<=p) if(getHASH(i,i+(j+1)/2-1)==getiHASH(i+j-1-(j+1)/2+1,i+j-1)) d[i]++;
for(int i=1;i<=p;i++) res+=d[i];
return res;
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
scanf("%s",s+1);n=strlen(s+1);pw[0]=1;for(int i=1;i<=n;i++) pw[i]=pw[i-1]*131;
K=read(),q=read();Segment2::build(1,1,n);
getANS(1,n);Segment1::build(1,1,n);
for(int i=1,op,l,r,ans,L,R;i<=q;i++){
op=read(),l=read(),r=read();
if(op==1){
scanf("%s",sss+1);
Segment2::upd(1,1,n,l,r,sss[1]);
if(l<=r-K+1) Segment1::upd(1,1,n,l,r-K+1,K);
L=r-K+2,R=r+K;L=max(L,1ll);R=min(R,n);
getANS(L,R);
for(int j=L;j<=r;j++) Segment1::upd(1,1,n,j,j,d[j-L+1]);
L=l-K+1,R=l+K;L=max(L,1ll);R=min(R,n);
getANS(L,R);
for(int j=L;j<l;j++) Segment1::upd(1,1,n,j,j,d[j-L+1]);
}else{
ans=0;
if(l<=r-K+1) ans+=Segment1::query(1,1,n,l,r-K+1);
ans+=getANS(max(r-K+2,l),r);
printf("%lld\n",ans);
}
}
return 0;
}
B.recollection
可以将从 \(u\) 到根的字符串看成从根到 \(u\) 的字符串,两种情况是等价的
这样给出的就是一颗 \(Trie\) 树,所以任意两个点的 \(lca\) 的深度就是他们的 \(lcp\) 的长度
再看 \(lcs\) 的长度如何求,可以根据 \(Trie\) 树直接建出广义 \(SAM\) 这样在 \(parent\) 树上 \(lca\) 的 \(len\) 就是 \(lcs\) 的长度
然后再根据 \(Trie\) 树上的位置去合并每一个点在 \(parent\) 树上的位置
此时每一对点的 \(lcp\) 都是一样的,只要最大化 \(lcs\) 即可
对于一些点他们 \(lca\) 最深的地方在 \(dfn\) 序相邻的两个地方产生
于是我们可以用启发式合并来合并子树,每次将小的插入到大的里面,再和前驱后继找到 \(lca\) 计算答案
Code
#include<bits/stdc++.h>
//#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,ans,tot=1;
int pos[400010],fa[400010],ch[400010],dep[200010];
unordered_map<int,int> tr[200010];
struct node{
int len,fa;
unordered_map<int,int> son;
}t[400010];
inline int extend(int c,int lst){
int p=++tot,f=lst;lst=p;
t[p].len=t[f].len+1;
while(f&&!t[f].son[c]) t[f].son[c]=p,f=t[f].fa;
if(!f) t[p].fa=1;
else{
int x=t[f].son[c],y=++tot;
if(t[f].len+1==t[x].len) t[p].fa=x,tot--;
else{
t[y]=t[x];t[y].len=t[f].len+1;t[x].fa=t[p].fa=y;
while(f&&t[f].son[c]==x) t[f].son[c]=y,f=t[f].fa;
}
}
return p;
}
inline void bfs(){
queue<int> q;
for(auto x:tr[1]) q.push(x.second);
pos[1]=1;
while(!q.empty()){
int x=q.front();q.pop();
pos[x]=extend(ch[x],pos[fa[x]]);
for(auto y:tr[x]) q.push(y.second);
}
}
namespace TCL{
int head[400010],ver[400010],to[400010],tot;
int dep[400010],fa[400010],top[400010],son[400010],siz[400010],dfn[400010],clo;
struct Data{int x;inline bool operator<(const Data &b)const{return dfn[x]<dfn[b.x];}};
set<Data>S[400010];
set<Data>::iterator iter;
namespace DSU{
int fa[400010];
inline void init(){for(int i=1;i<=tot;i++) fa[i]=i;}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
}
inline void add(int x,int y){
ver[++tot]=y;
to[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int fath,int depth){
dep[x]=depth,fa[x]=fath,siz[x]=1;
int maxson=-1;
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fath) continue;
dfs1(y,x,depth+1);
siz[x]+=siz[y];
if(siz[y]>maxson) son[x]=y,maxson=siz[y];
}
}
void dfs2(int x,int topf){
top[x]=topf;
dfn[x]=++clo;
if(!son[x]) return ;
dfs2(son[x],topf);
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int LCA(int x,int y){
while(top[x]!=top[y]) (dep[top[x]]>dep[top[y]])?(x=fa[top[x]]):(y=fa[top[y]]);
return (dep[x]<dep[y])?(x):(y);
}
inline void merge(int x,int y,int len){
x=DSU::getfa(x),y=DSU::getfa(y);
if(S[x].size()>S[y].size()) swap(x,y);
DSU::fa[x]=y;
int lca;
for(auto p:S[x]){
S[y].insert(p);
iter=S[y].find(p);
if(iter!=S[y].begin()){
iter--;
lca=LCA(p.x,iter->x);
ans=max(ans,t[lca].len+len);
iter++;
}
iter++;
if(iter!=S[y].end()){
lca=LCA(p.x,iter->x);
ans=max(ans,t[lca].len+len);
}
}
}
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
n=read();
for(int i=2,u,k;i<=n;i++){
u=read(),k=read();
dep[i]=dep[u]+1;tr[u][k]=i;ch[i]=k;fa[i]=u;
}
bfs();
for(int i=tot;i;i--) TCL::add(t[i].fa,i);
TCL::dfs1(1,0,1);TCL::dfs2(1,1);
TCL::DSU::init();
for(int i=2;i<=n;i++) TCL::S[pos[i]].insert((TCL::Data){pos[i]});
for(int i=n;i>=2;i--) TCL::merge(pos[i],pos[fa[i]],dep[fa[i]]);
printf("%d\n",ans);
return 0;
}
C.回忆树
直接上树不好搞,考虑在序列上怎么搞,相当于给定区间问出现的给定字符串的数量
那我们直接用右端点的答案减去左端点的答案就行
对于单个询问串可以建立 \(kmp\) ,那多个询问串可以对他们建立 \(AC\) 自动机
那么对于每一个字符串他的出现次数就是 \(fail\) 树上的子树和
再将这种方法拓展上树 发现一共会出现三种情况
1.在 \(u\) 到 \(lca\) 的路径上出现询问串的反串
2.在 \(lca\) 到 \(v\) 的路径上出现询问串
3.跨过 \(lca\) 的部分出现,一部分正串,另一部分反串
对于第三种情况,可以直接提取出来暴力匹配
前两种情况可以将询问串正反都插入,然后再预处理出来到每个节点(原树)在 \(fail\) 树上匹配到的数量
可以用可持久化线段树求
Code
#include<bits/stdc++.h>
//define int long long
#define uint unsigned long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
inline char sread(){
char ch=getchar();
while(ch<'a'||ch>'z') ch=getchar();
return ch;
}
int n,m,len,cnt;
int head[100010],from[200010],ver[200010],to[200010],edge[200010],tot;
int ch[200010];
int fa[100010][21],rt[100010],dep[100010],siz[100010],son[100010],top[100010];
int tr[600010][26],fail[600010];
uint HASH[300010],pw[300010],SUBHASH;
inline uint getHASH(int l,int r){return HASH[r]-HASH[l-1]*pw[r-l+1];}
char C,st[600010];
struct Query{
int u,v,len,pos1,pos2;
vector<char> s;
}Q[100010];
namespace FailTree{
#define lson t[x].ls
#define rson t[x].rs
int dfn[600010],siz[600010],cnt,clo;
int head[600010],ver[600010],to[600010],tot;
struct Seg{int sum,ls,rs;}t[600010*20];
inline void add(int x,int y){
ver[++tot]=y;
to[tot]=head[x];
head[x]=tot;
}
void ins(int &x,int pre,int l,int r,int pos){
x=++cnt;t[x]=t[pre];t[x].sum++;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) ins(lson,t[pre].ls,l,mid,pos);
else ins(rson,t[pre].rs,mid+1,r,pos);
}
int query(int u,int v,int l,int r,int L,int R){
if(L<=l&&r<=R) return t[v].sum-t[u].sum;
int mid=(l+r)>>1,res=0;
if(L<=mid) res+=query(t[u].ls,t[v].ls,l,mid,L,R);
if(R>mid) res+=query(t[u].rs,t[v].rs,mid+1,r,L,R);
return res;
}
void dfs(int x,int fa){
dfn[x]=++clo,siz[x]=1;
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fa) continue;
dfs(y,x);
siz[x]+=siz[y];
}
}
#undef lson
#undef rson
}
inline void add(int x,int y,int k){
ver[++tot]=y;
from[tot]=x;
edge[tot]=k;
to[tot]=head[x];
head[x]=tot;
}
inline void ins(int id){
int p=0;
for(int i=1,v;i<=len;i++){
v=st[i]-'a';
if(!tr[p][v]) tr[p][v]=++cnt;
p=tr[p][v];
}
Q[id].pos1=p;
reverse(st+1,st+1+len);p=0;
for(int i=1,v;i<=len;i++){
v=st[i]-'a';
if(!tr[p][v]) tr[p][v]=++cnt;
p=tr[p][v];
}
Q[id].pos2=p;
}
inline void build(){
queue<int> q;
for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;i++){
if(tr[x][i]) fail[tr[x][i]]=tr[fail[x]][i],q.push(tr[x][i]);
else tr[x][i]=tr[fail[x]][i];
}
}
}
void dfs(int x,int fath,int p){
fa[x][0]=fath;dep[x]=dep[fath]+1;siz[x]=1;
int maxson=-1;
for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
FailTree::ins(rt[x],rt[fath],1,cnt+1,FailTree::dfn[p]);
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fath) continue;
dfs(y,x,tr[p][edge[i]]);
siz[x]+=siz[y];
if(siz[y]>maxson) maxson=siz[y],son[x]=y;
}
}
void dfs2(int x,int topf){
top[x]=topf;
if(!son[x]) return ;
dfs2(son[x],topf);
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fa[x][0]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int Kfa(int x,int k){
for(int i=20;~i;i--) if(k>=(1<<i)) x=fa[x][i],k-=(1<<i);
return fa[x][0];
}
inline int LCA(int x,int y){
while(top[x]!=top[y]) (dep[top[x]]>dep[top[y]])?(x=fa[top[x]][0]):(y=fa[top[y]][0]);
return (dep[x]<dep[y])?(x):(y);
}
inline int HASHmatch(int id,int u,int v,int lca){
int p=0,pp,res=0;while(u!=lca) st[++p]=ch[u],u=fa[u][0];
pp=p;while(v!=lca) st[++p]=ch[v],v=fa[v][0];
reverse(st+pp+1,st+p+1);
len=Q[id].len;SUBHASH=0;
for(int i=1;i<=p;i++) HASH[i]=HASH[i-1]*131+st[i];
for(int i=1;i<=Q[id].len;i++) SUBHASH=SUBHASH*131+Q[id].s[i]-'a';
for(int i=1;i<=p-len+1;i++) if(getHASH(i,i+len-1)==SUBHASH) res++;
return res;
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
pw[0]=1;for(int i=1;i<=300000;i++) pw[i]=pw[i-1]*131;
n=read(),m=read();
for(int i=1,x,y;i<n;i++){
x=read(),y=read(),C=sread();
add(x,y,C-'a'),add(y,x,C-'a');
}
for(int i=1;i<=m;i++){
Q[i].u=read();
Q[i].v=read();
scanf("%s",st+1);len=strlen(st+1);
Q[i].s.emplace_back('~');Q[i].len=len;
for(int j=1;j<=len;j++) Q[i].s.emplace_back(st[j]);
ins(i);
}
build();
for(int i=cnt;i;i--) FailTree::add(fail[i],i);
FailTree::dfs(0,0);dfs(1,0,1);dfs2(1,1);
for(int i=1,x,y;i<=tot;i+=2){x=from[i],y=ver[i];if(dep[x]>dep[y]) ch[x]=edge[i];else ch[y]=edge[i];}
for(int i=1,u,v,lca,Lv,kf,ans,k1,k2;i<=m;i++){
ans=0;u=Q[i].u,v=Q[i].v;k1=u,k2=v;
len=Q[i].len;lca=LCA(u,v);
Lv=dep[u]-dep[lca]-Q[i].len;
if(Lv>=0){k1=kf=Kfa(u,Lv);ans+=FailTree::query(rt[kf],rt[u],1,cnt+1,FailTree::dfn[Q[i].pos2],FailTree::dfn[Q[i].pos2]+FailTree::siz[Q[i].pos2]-1);}
Lv=dep[v]-dep[lca]-Q[i].len;
if(Lv>=0){k2=kf=Kfa(v,Lv);ans+=FailTree::query(rt[kf],rt[v],1,cnt+1,FailTree::dfn[Q[i].pos1],FailTree::dfn[Q[i].pos1]+FailTree::siz[Q[i].pos1]-1);}
if(lca!=u&&lca!=v) ans+=HASHmatch(i,k1,k2,lca);
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号