【TJOI2015】BZOJ3998 弦论题解(SAM+DP)

题目:BZOJ3998.
题目大意:给定一个串 S S S和一个数 T T T,求 S S S的第 k k k小子串,若 T = 0 T=0 T=0表示子串重复算一个,否则不算一个,若子串数不到 k k k个输出 − 1 -1 1.
1 ≤ ∣ S ∣ ≤ 5 ∗ 1 0 5 , 0 ≤ T ≤ 1 1\leq |S|\leq 5*10^5,0\leq T\leq 1 1S5105,0T1.

首先看到子串就对 S S S建立SAM.

建立SAM后,我们根据 T T T分类讨论,先考虑 T = 0 T=0 T=0的情况.

首先着第 k k k小的子串可以先考虑直接从原点出发,从小到大枚举所有字符转移边.若这条字符转移边到达的状态可以构成的子串数量不足 k k k个,则肯定不往这条字符转移边转移,直接让 k k k减掉这个值,然后尝试下一条字符转移边;否则进入这条字符转移边即可.

考虑何时停止,很明显通过之前的路径到达当前状态构成的子串是唯一的,所以只要当前 k k k 1 1 1就可以停止了.

再来考虑如何求出每一个状态开始可以构成的子串数量,很明显我们可以用DP来预处理出这些值,设 f [ i ] f[i] f[i]为到状态 i i i可以构成的数量,那么:
f [ x ] = 1 + ∑ ( x , y ) ∈ E f [ y ] f[x]=1+\sum_{(x,y)\in E}f[y] f[x]=1+(x,y)Ef[y]

T = 1 T=1 T=1的情况其实类似,只是这样子一个状态可以表示的子串数量就要乘上它所代表的Right集合大小了.

时间复杂度 O ( ∣ S ∣ Σ ) O(|S|\Sigma) O(SΣ).

代码如下:

#include<bits/stdc++.h> 
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=500000,C=26;

int n,t,k;
char c[N+9],sc[N+9];
struct automaton{
  int s[C],len,par;
}tr[N*2+9];
int cn,last,rght[N*2+9];

void Build_sam(){last=cn=1;}

void extend(int x){
  int np=++cn,p=last;
  tr[np].len=tr[p].len+1;rght[np]=1;
  last=np;
  while (p&&!tr[p].s[x]) tr[p].s[x]=np,p=tr[p].par;
  if (!p) tr[np].par=1;
  else{
  	int q=tr[p].s[x];
  	if (tr[p].len+1==tr[q].len) tr[np].par=q;
  	else{
	  tr[++cn]=tr[q];tr[cn].len=tr[p].len+1;
	  tr[q].par=tr[np].par=cn;
	  while (p&&tr[p].s[x]==q) tr[p].s[x]=cn,p=tr[p].par;
	}
  }
}

int q[N*2+9],v[N+9],sum[N*2+9];

void pre(){
  int tt;
  for (int i=1;i<=cn;++i) ++v[tr[i].len];
  for (int i=1;i<=n;++i) v[i]+=v[i-1];
  for (int i=cn;i>=1;--i) q[v[tr[i].len]--]=i;
  for (int i=cn;i>=1;--i)
    if (t==1) rght[tr[q[i]].par]+=rght[q[i]];
    else rght[q[i]]=1;
  rght[1]=0;
  for (int i=cn;i>=1;--i){
    tt=q[i];sum[tt]=rght[tt];
	for (int j=0;j<C;++j)
      sum[tt]+=sum[tr[tt].s[j]];
  }
}

void dfs(int x,int pos){
  if (k<=rght[x]) {k=0;return;}
  k-=rght[x];
  for (int i=0;i<C;++i)
    if (tr[x].s[i]){
      if (k<=sum[tr[x].s[i]]){
      	sc[pos]='a'+i;
      	dfs(tr[x].s[i],pos+1); 
	    return;
	  }
	  k-=sum[tr[x].s[i]];
    }
}

Abigail into(){
  scanf("%s",c+1);
  n=strlen(c+1);
  scanf("%d%d",&t,&k);
}

Abigail work(){
  Build_sam();
  for (int i=1;i<=n;++i)
    extend(c[i]-'a');
  pre();
  dfs(1,1);
}

Abigail outo(){
  k>0?puts("-1"):printf("%s\n",sc+1);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值