【校内模拟】string (SAM)(DSU on tree)

Deadly 提交于 2020-03-01 08:21:54

简要题意:

给一个串,询问有多少个区间可以由某一个串重复 kk 次得到。


题解:

首先很容易发现就是询问有多少个子串有 n/kn/k 的循环节。

也就是说要有 nn/kn-n/k 的 border。

也就是求由多少对 l,rl,r,满足 lcs(l,r)(rl)(k1)lcs(l,r)\ge (r-l)\cdot(k-1)

直接建立SAM然后DSU on tree 即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=3e5+7;

int n,k;
char s[N];

namespace SAM{

cs int N=::N<<1|1;

int fa[N],len[N],nw;
void init(){
	for(int re i=1;i<=n;++i)
		len[i]=i;nw=n+1;
}void ins(int i,int c){
	static int son[N][26];
	int cur=i,p=(i-1)?(i-1):(n+1);
	for(;p&&!son[p][c];p=fa[p])
		son[p][c]=cur;
	if(!p)fa[cur]=n+1;
	else {
		int q=son[p][c];
		if(len[q]==len[p]+1)fa[cur]=q;
		else {
			int nq=++nw,q=son[p][c];
			memcpy(son[nq],son[q],sizeof son[q]);
			len[nq]=len[p]+1,fa[nq]=fa[q];fa[q]=fa[cur]=nq;
			for(;p&&son[p][c]==q;p=fa[p])
				son[p][c]=nq;
		}
	}
}

int bin[N],nd[N];
void radix_sort(){
	for(int re i=1;i<=nw;++i)++bin[len[i]];
	for(int re i=1;i<=n;++i)bin[i]+=bin[i-1];
	for(int re i=nw;i;--i)nd[bin[len[i]]--]=i;
}

std::vector<int> q[N];
int el[N],nx[N];
int sz[N],son[N],top[N];
void init_fail(){
	for(int re i=nw;i;--i){
		int u=nd[i];sz[fa[u]]+=++sz[u];
		if(sz[u]>sz[son[fa[u]]])son[fa[u]]=u;
		if(u!=n+1)nx[u]=el[fa[u]],el[fa[u]]=u;
	}for(int re i=1;i<=nw;++i){
		int u=nd[i];if(!top[u])top[u]=u;
		if(son[u])top[son[u]]=top[u];
	}for(int i=1;i<=n;++i){
		int u=i;while(u){
			q[top[u]].push_back(i);
			u=fa[top[u]];
		}
	}
}

int tr[::N],st[::N],tp;
void add(int p){
	st[++tp]=p;
	for(;p<=n;p+=p&-p)++tr[p];
}int qy(int p){
	int r=0;for(;p;p^=p&-p)r+=tr[p];
	return r;
}void clr(){
	while(tp){
		int p=st[tp--];
		for(;p<=n;p+=p&-p)tr[p]=0;
	}
}int qy(int l,int r){
	return qy(std::min(r,n))-qy(std::max(l,1)-1);
}

int rg;ll ans;

void dfs(int u){
	for(int re v=el[u];v;v=nx[v])
		if(v!=son[u])dfs(v),clr();
	if(son[u])dfs(son[u]);rg=len[u]/k;
	for(int re v=el[u];v;v=nx[v])
		if(v!=son[u]){
			for(int re w:q[v])
				ans+=qy(w-rg,w+rg);
			for(int re w:q[v])add(w);
		}
	if(u<=n)
		ans+=qy(u-rg,u+rg),add(u);
}

}//namespace SAM


void Main(){
	scanf("%d%d",&n,&k);--k;
	scanf("%s",s+1);SAM::init();
	for(int re i=1;i<=n;++i)
		SAM::ins(i,s[i]-'a');
	SAM::radix_sort();SAM::init_fail();
	SAM::dfs(n+1);cout<<SAM::ans<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("sutoringu.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("sutoringu.in","r",stdin);
	freopen("sutoringu.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!