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

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/88687227

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

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

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

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

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

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

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

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

代码如下:

#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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/88687227