• 【洛谷P3975】弦论


    题目

    题目链接:https://www.luogu.com.cn/problem/P3975
    为了提高智商,ZJY 开始学习弦论。这一天,她在《String theory》中看到了这样一道问题:对于一个给定的长度为 (n) 的字符串,求出它的第 (k) 小子串是什么。你能帮帮她吗?
    (t)(0) 则表示不同位置的相同子串算作一个,(t)(1) 则表示不同位置的相同子串算作多个。

    思路

    建出 SAM 之后,我们分开讨论 (t=0)(t=1) 的点。
    对于 (t=0) 的点,我们不难发现经过一个点本质不同的子串的数量就是其 SAM 的 DAG 上所能到达的点。因为在这个类之前所有串的后缀都是一致的,而从 DAG 上这个类可以到达的点也就意味着有多少个不同的“前缀”。所以我们直接在 DAG 上记忆化搜索即可。
    对于 (t=1) 的点,经过一个节点的位置不同的子串的数量就是他所代表的类的集合大小。那么我们可以在 parent 树上拓扑求出每一个节点的子树大小,然后再记忆化搜索。
    询问的时候,枚举当前位每一个字符,如果数量不够就减去它的数量,否则输出它继续递归。
    时间复杂度 (O(nk)),其中 (k=26)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1000010;
    int n,k,t,a[N];
    ll cnt[N];
    char s[N];
    
    struct SAM
    {
    	int tot,last,ch[N][26],fa[N],len[N];
    	ll siz[N];
    	SAM() { tot=last=1; }
    	
    	void ins(int c)
    	{
    		int p=last,np=++tot; 
    		last=np; len[np]=len[p]+1; siz[np]=1;
    		for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
    		if (!p) fa[np]=1;
    		else
    		{
    			int q=ch[p][c];
    			if (len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tot;
    				len[nq]=len[p]+1; fa[nq]=fa[q];
    				for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
    				fa[q]=fa[np]=nq;
    				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
    			}
    		}
    	}
    	
    	void topsort()
    	{
    		for (int i=1;i<=tot;i++) cnt[len[i]]++;
    		for (int i=1;i<=tot;i++) cnt[i]+=cnt[i-1];
    		for (int i=1;i<=tot;i++) a[cnt[len[i]]--]=i;
    		for (int i=tot;i>=1;i--) siz[fa[a[i]]]+=siz[a[i]];
    		memset(cnt,0,sizeof(cnt));
    	}
    	
    	void dfs(int x)
    	{
    		if (cnt[x]) return;
    		if (t) cnt[x]=siz[x];
    			else cnt[x]=siz[x]=(x>1);
    		for (int i=0;i<26;i++)
    			if (ch[x][i])
    			{
    				dfs(ch[x][i]);
    				cnt[x]+=cnt[ch[x][i]];
    			}
    	}
    	
    	void query(int x,int k)
    	{
    		if (k<=siz[x]) return;
    		k-=siz[x];
    		for (int i=0;i<26;i++)
    			if (k>cnt[ch[x][i]]) k-=cnt[ch[x][i]];
    			else
    			{
    				putchar(i+'a');
    				query(ch[x][i],k);
    				return;
    			}
    	}
    }sam;
    
    int main()
    {
    	scanf("%s%d%d",s+1,&t,&k);
    	n=strlen(s+1);
    	for (int i=1;i<=n;i++)
    		sam.ins(s[i]-'a');
    	if (t) sam.topsort();
    	cnt[1]=sam.siz[1]=0;
    	sam.dfs(1);
    	if (k>cnt[1]) printf("-1");
    		else sam.query(1,k);
    	return 0;
    }
    
  • 相关阅读:
    android studio无线真机调试------Android
    新建文件夹和文件,并向文件中写入数据---------Android
    wpf获取鼠标的位置-------WPF
    React Native环境搭建
    页面定制CSS代码
    视图优化
    内存优化
    电量优化
    轻量容器、枚举的使用
    AndroidAnnotations框架
  • 原文地址:https://www.cnblogs.com/stoorz/p/14254034.html
Copyright © 2020-2023  润新知