专项测试 字符串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  阅读(122)  评论(0)    收藏  举报