码迷,mamicode.com
首页 > 编程语言 > 详细

P3975 [TJOI2015]弦论 SAM+right数组

时间:2020-12-23 11:33:36      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:字符   证明   turn   amp   end   bfs   c++   one   http   

题意:

戳这里

分析:

\(sam\) 裸题,求第 \(k\) 大字符串

首先建出 \(sam\) 然后求出 \(siz[i]\) 表示 \(i\) 节点代表的串的 \(endpos\) 的集合大小

然后分情况讨论:

  1. \(T==0\) 只统计本质不同的串的个数,所以所有点的 \(siz[i]\) 都置为 1
  2. \(T==1\) \(siz[i]\) 不变

按照长度进行拓扑排序,因为 \(sam\) 上的点的 \(parent\) 树的 \(BFS\) 序等价于将 \(len\) 值类似于 \(sa\) 的基数排序后得到的序列一样(博主太笨,不会证明)

然后按照拓扑序,将儿子的 \(siz\) 值累加到父亲节点上,就得到经过每个点(即每种子串的出现次数),然后 \(DFS\) 得到第 \(k\) 个就可以了

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 5e5+5;
	int len[maxn<<1],siz[maxn<<1],sum[maxn<<1],trans[maxn<<1][27],link[maxn<<1];
	int t[maxn<<1],sa[maxn<<1]; 
	int n,T,k,cnt=1,lst=1;
	char ch[maxn];
	
	void insert(int x)
	{
		int cur=++cnt;
		int tmp=lst;lst=cur;
		len[cur]=len[tmp]+1;siz[cur]=1;
		for(;tmp&&!trans[tmp][x];tmp=link[tmp]) trans[tmp][x]=cur;
		if(!tmp) link[cur]=1;
		else
		{
			int q=trans[tmp][x];
			if(len[tmp]+1==len[q]) link[cur]=q;
			else
			{
				int clone=++cnt;
				len[clone]=len[q];link[clone]=link[q];
				memcpy(trans[clone],trans[q],sizeof(trans[q]));
				link[cur]=link[q]=clone;
				len[clone]=len[tmp]+1;
				for(;tmp&&trans[tmp][x]==q;tmp=link[tmp]) trans[tmp][x]=clone;
			}
		}
		
	}
	
	void solve(int u,int k)
	{
		if(k<=siz[u]) return ;
		k-=siz[u];
		for(int i=0;i<26;i++)
		{
			if(!trans[u][i]) continue;
			if(k>sum[trans[u][i]]) k-=sum[trans[u][i]];
			else
			{
				putchar(i+‘a‘);
				solve(trans[u][i],k);
				return;
			}	
		}
	}
	
	void work()
	{
		scanf("%s",ch+1);n=strlen(ch+1);scanf("%d%d",&T,&k);
		for(int i=1;i<=n;i++) insert(ch[i]-‘a‘);
		for(int i=1;i<=cnt;i++) t[len[i]]++;
		for(int i=1;i<=cnt;i++) t[i]+=t[i-1];
		for(int i=1;i<=cnt;i++) sa[t[len[i]]--]=i;
		for(int i=cnt;i>=1;i--) siz[link[sa[i]]]+=siz[sa[i]];
		for(int i=1;i<=cnt;i++) T==0?(sum[i]=siz[i]=1):(sum[i]=siz[i]);
		siz[1]=sum[1]=0;
		for(int i=cnt;i>=1;i--)
		{
			sum[sa[i]]=siz[sa[i]];
			for(int j=0;j<26;j++)
			{
				if(trans[sa[i]][j]) sum[sa[i]]+=sum[trans[sa[i]][j]];
			}
		}
		if(sum[1]<k) printf("-1\n");
		else solve(1,k);
	}
	
}

int main()
{
	zzc::work();
	return 0;
} 

P3975 [TJOI2015]弦论 SAM+right数组

标签:字符   证明   turn   amp   end   bfs   c++   one   http   

原文地址:https://www.cnblogs.com/youth518/p/14152595.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!