【解题报告】SPOJ - SUBLEX Lexicographical Substring Search 后缀自动机

给定字符串S,Q个询问,每次询问第k大的字符串(去重)

思路:首先我们知道自动机里存了所有不同的子串,如果我们询问第k大直接dfs找是肯定要炸的,所以考虑在每个结点处先预处理出来经过这条路能找到几个字符串,然后在找第k大的时候就可以直接判断走不走这条路径。

几点细节:每个结点基础就会有一个路径,因为直接走一个字符能到这个结点;找第k大的时候会有一些+1或者-1的操作,因为起始节点是空字符串。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
char s[maxn];
int len;
struct SAM
{
    int mp[maxn][30];int fa[maxn];int ed;int ct;int len[maxn];
    //mp是SAM的图,fa是那颗树
	//ed是insert之前最后的那个结点
	//ct是用来计数的,跟建trie树里的那个tot一个性质
	//len[i]表示i结点的maxlen,minlen不用记录,直接能算
	SAM(){ed=ct=1;}
    inline void ins(int c) 
    {
        int p=ed;//p相当于不算这个新建的点是最后一个点,算上新建的这个点是倒数第二个点 
		ed=++ct;
		len[ed]=len[p]+1;//先初始化size和len 
        for(;p&&mp[p][c]==0;p=fa[p]){
			mp[p][c]=ed;
		}//然后顺着parent树的路径向上找 
        if(p==0){//第一种情况:根作为其父节点 
			fa[ed]=1;
			return;
		}
		int q=mp[p][c];
        if(len[p]+1==len[q]){//第二种情况:别的结点作为其父节点 
			fa[ed]=q;
			return;
		}
        len[++ct]=len[p]+1;//第三种情况:加上天上的点 
        fa[ct]=fa[q];
		fa[q]=ct;
		fa[ed]=ct;
		for(int i=0;i<26;i++){
			mp[ct][i]=mp[q][i];
		}
        for(int i=p;mp[i][c]==q;i=fa[i]){
			mp[i][c]=ct;
		}
    } 
}sam;
int cnt[maxn];
int dfs1(int u)
{
	//cout<<u<<endl; 
	if(cnt[u]) return cnt[u];
	cnt[u]=1;
	for(int i=0;i<26;i++){
		if(!sam.mp[u][i]) continue;
		//cout<<u<<" "<<sam.mp[u][i]<<endl;
		cnt[u]+=dfs1(sam.mp[u][i]);
	}
	return cnt[u];
} 
void dfs2(int k,int u)
{
	k--;
	if(!k){
		puts("");
		return;
	}
	for(int i=0;i<26;i++){
		if(!sam.mp[u][i]) continue;
		if(cnt[sam.mp[u][i]]<k){
			k-=cnt[sam.mp[u][i]];
			continue;
		}
		putchar(i+'a');
		dfs2(k,sam.mp[u][i]);
		break;
	}
}
int main()
{
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1;i<=len;i++){sam.ins(s[i]-'a');}
    dfs1(1);
    int q;scanf("%d",&q);
    int num;
    while(q--){
    	scanf("%d",&num);
    	dfs2(num+1,1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值