loj6031.「雅礼集训 2017 Day1」字符串

题意

注意到询问串的长度\(k\)是给定的,同时\(\sum\limits len_w=kq\leqslant10^5\),我们发现\(k,q\)之间一个大了另一个必定会小,因此我们对\(k\)分类讨论:
首先肯定要对\(s\)建一个后缀自动机,对每个点维护\(endpos\)集合的大小。

\(k\geqslant\sqrt{n}\)

这时\(q\leqslant\sqrt{n}\),我们直接在\(SAM\)暴力匹配\(w\),找出所有\(w\)的前缀对应的节点,之后枚举询问,倍增跳到\(w[l_i...r_i]\)这个节点,将答案加上该节点的\(endpos\)集合大小即可。

\(k<\sqrt{n}\)

这时\(m\)组询问中的种类很少,我们可以枚举\(w\)的所有子串,算出它在\([l,r]\)这段询问区间中出现的次数,乘上它的答案加到答案中。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
#define pii pair<int,int>
#define mkp make_pair
#define fir first
#define sec second
const int maxn=2e5+10;
const int maxt=410;
int n,m,Q,K,tot,cnt_edge,t;
int head[maxn],size[maxn],pos[maxn],posl[maxn];
int f[maxn][18];
char s[maxn];
inline int read()
{
	char c=getchar();re int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
struct edge{int to,nxt;}e[maxn];
inline void add_edge(int u,int v)
{
	e[++cnt_edge].nxt=head[u];
	head[u]=cnt_edge;
	e[cnt_edge].to=v;
}
struct Query{int l,r;}qr[maxn];
struct SAM
{
	int last,tot;
	int fa[maxn],len[maxn];
	int ch[maxn][26];
	SAM(){last=tot=1;}
	inline void add(int c)
	{
		re int now=++tot;len[now]=len[last]+1;
		re int p=last;last=now;
		while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
		if(!p){fa[now]=1;return;}
		re int q=ch[p][c];
		if(len[q]==len[p]+1)fa[now]=q;
		else
		{
			re int nowq=++tot;len[nowq]=len[p]+1;
			memcpy(ch[nowq],ch[q],sizeof(ch[q]));
			fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
			while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
		}
	}
}sam;
void dfs(int x)
{
	for(re int i=1;i<=17;i++)f[x][i]=f[f[x][i-1]][i-1];
	for(re int i=head[x];i;i=e[i].nxt)
	{
		re int y=e[i].to;
		f[y][0]=x;dfs(y);
		size[x]+=size[y];
	}
}
inline void solve1()
{
	vector<int>a[maxt][maxt];
	for(int i=1;i<=m;i++)a[qr[i].l][qr[i].r].push_back(i);
	while(Q--)
	{
		scanf("%s",s+1);
		int l=read()+1,r=read()+1;
		ll res=0;
		for(int i=1;i<=K;i++)
			for(int j=i,now=1;j<=K;j++)
			{
				now=sam.ch[now][s[j]-'a'];
				if(!now)break;
				int ql=lower_bound(a[i][j].begin(),a[i][j].end(),l)-a[i][j].begin();
				int qr=upper_bound(a[i][j].begin(),a[i][j].end(),r)-a[i][j].begin();
				res+=1ll*(qr-ql)*size[now];
			}
		printf("%lld\n",res);
	}
}
inline int find(int l,int r)
{
	re int now=pos[r];
	for(re int i=17;~i;i--)if(sam.len[f[now][i]]>=min(r-l+1,posl[r]))now=f[now][i];
	return now;
}
inline int solve(int l,int r)
{
	if(!pos[r]||posl[r]<(r-l+1))return 0;;
	re int now=find(l,r);
	if(sam.len[now]>=r-l+1)return size[now];
	else return 0;
}
inline void solve2()
{
	while(Q--)
	{
		scanf("%s",s+1);
		re int l=read()+1,r=read()+1;
		re int now=1,nowl=0,res=0;
		for(re int i=1;i<=K;i++)
		{
			re int c=s[i]-'a';
			while(!sam.ch[now][c]&&now)now=sam.fa[now],nowl=sam.len[now];
			if(!now){now=1;pos[i]=1;nowl=0;posl[i]=0;continue;}
			now=sam.ch[now][c];pos[i]=now;
			nowl++;posl[i]=nowl;
		}	
		for(re int i=l;i<=r;i++)res+=solve(qr[i].l,qr[i].r);
		printf("%d\n",res);
	}
}
int main()
{
	//freopen("test.in","r",stdin);
	//freopen("test.out","w",stdout);
	n=read(),m=read(),Q=read(),K=read();t=330;
	scanf("%s",s+1);
	for(re int i=1;i<=n;i++)sam.add(s[i]-'a'),size[sam.last]=1;
	for(re int i=2;i<=sam.tot;i++)add_edge(sam.fa[i],i);
	dfs(1);
	for(re int i=1;i<=m;i++)qr[i].l=read()+1,qr[i].r=read()+1;
	if(K<=t)solve1();
	else solve2();
	return 0;
} 
posted @ 2020-01-04 17:21  nofind  阅读(206)  评论(0编辑  收藏  举报