专项测试 字符串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;
}
posted @ 2021-12-12 20:05  Max_QAQ  阅读(98)  评论(0)    收藏  举报