• [TJOI2015]弦论(第k小子串)


    题意:
    对于一个给定的长度为n的字符串,求出它的第k小子串。
    有参数t,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。

    题解:
    首先,因为t的原因,后缀数组较难实现,这里不讨论。

    使用后缀自动机:
    因为,这里需要按字典序考虑子串,所以要使用trs指针。
    首先,计算出每个子串的贡献:t=0则为1,t=1则为出现次数。
    然后,通过记搜算出匹配到每个点之后可以形成多少贡献。因为使用trs,无需考虑压缩。
    最后,在每个节点处找到唯一一个应当向下计算的点,循环直到找到解。

    代码:

    #include <stdio.h>
    #define ll long long
    int trs[1000010][26],fa[1000010],len[1000010],sl=1,la=1;
    int su[1000010];ll dp[1000010];
    void insert(int c)
    {
    	int np=++sl,p=la;
    	len[np]=len[la]+1;la=np;
    	while(p!=0&&trs[p][c]==0)
    	{
    		trs[p][c]=np;
    		p=fa[p];
    	}
    	if(p==0)
    		fa[np]=1;
    	else
    	{
    		int q=trs[p][c];
    		if(len[q]==len[p]+1)
    			fa[np]=q;
    		else
    		{
    			int nq=++sl;
    			len[nq]=len[p]+1;
    			fa[nq]=fa[q];fa[q]=fa[np]=nq;
    			for(int i=0;i<26;i++)
    				trs[nq][i]=trs[q][i];
    			while(p!=0&&trs[p][c]==q)
    			{
    				trs[p][c]=nq;
    				p=fa[p];
    			}
    		}
    	}
    	su[la]=1;
    }
    int fr[1000010],ne[1000010],v[1000010],bs=0;
    void addb(int a,int b)
    {
    	v[bs]=b;
    	ne[bs]=fr[a];
    	fr[a]=bs++;
    }
    void build()
    {
    	for(int i=1;i<=sl;i++)
    		fr[i]=-1;
    	for(int i=2;i<=sl;i++)
    		addb(fa[i],i);
    }
    void dfs0(int u)
    {
    	for(int i=fr[u];i!=-1;i=ne[i])
    	{
    		dfs0(v[i]);
    		su[u]|=su[v[i]];
    	}
    }
    void dfs1(int u)
    {
    	for(int i=fr[u];i!=-1;i=ne[i])
    	{
    		dfs1(v[i]);
    		su[u]+=su[v[i]];
    	}
    }
    void dfs2(int u)
    {
    	if(dp[u])
    		return;
    	dp[u]=su[u];
    	for(int i=0;i<26;i++)
    	{
    		if(trs[u][i])
    		{
    			dfs2(trs[u][i]);
    			dp[u]+=dp[trs[u][i]];
    		}
    	}
    }
    char zf[500010];
    int main()
    {
    	int t,k,u=1;
    	scanf("%s%d%d",zf,&t,&k);
    	for(int i=0;zf[i]!=0;i++)
    		insert(zf[i]-'a');
    	build();
    	if(t==0)dfs0(1);
    	else dfs1(1);
    	dfs2(1);
    	if(dp[1]-su[1]<k)
    	{
    		printf("-1");
    		return 0;
    	}
    	while(1)
    	{
    		if(u!=1)
    		{
    			if(su[u]>=k)
    				break;
    			k-=su[u];
    		}
    		int i;
    		for(i=0;i<26;i++)
    		{
    			if(trs[u][i]==0)
    				continue;
    			if(dp[trs[u][i]]>=k)
    				break;
    			k-=dp[trs[u][i]];
    		}
    		printf("%c",i+'a');
    		u=trs[u][i];
    	}
    	return 0;
    }
    
  • 相关阅读:
    使用批处理查找某个文件夹及其子文件夹(递归地)中存在的空文件夹
    pl/sql连接oracle
    JSON对象转换成url参数
    创建maven工程
    线程同步
    java.lang.Object 的 wait 和 notify方法,及关键字 synchronized的作用
    Thread操作
    java.lang.io包的使用
    用程序打印汉字
    java程序员应该熟练掌握的技术
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11616301.html
Copyright © 2020-2023  润新知