bzoj 3998 弦论 —— 后缀自动机

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3998

关于相同子串算一个还是算多个,其实就是看一种状态的 right 集合是否加上 Parent 树中子树的 right 集合;

虽然是子串却不管 l 数组,因为实际上 dfs 走到一个点,得到的是一个确定的子串,而这个子串的状态属于这个点表示的状态,l 数组是这个点表示的状态数,当然不用考虑;

记一个 sum 表示这个状态往后加字母能得到的所有子串个数,然后在SAM上按字典序 dfs ,走过的边就是添加的字符。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int const xn=5e5+5;
int T,K,fa[xn<<1],l[xn<<1],go[xn<<1][30],lst=1,cnt=1,siz[xn<<1],sum[xn<<1];
int tax[xn<<1],t[xn<<1];
char s[xn];
void add(int w)
{
  int p=lst,np=++cnt; lst=np; l[np]=l[p]+1; siz[np]=1;
  for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
  if(!p)fa[np]=1;
  else
    {
      int q=go[p][w];
      if(l[q]==l[p]+1)fa[np]=q;
      else
    {
      int nq=++cnt; l[nq]=l[p]+1;
      memcpy(go[nq],go[q],sizeof go[q]);
      fa[nq]=fa[q]; fa[q]=fa[np]=nq;
      for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
    }
    }
}
void Tsort()
{
  for(int i=1;i<=cnt;i++)tax[l[i]]++;
  for(int i=1;i<=cnt;i++)tax[i]+=tax[i-1];
  for(int i=1;i<=cnt;i++)t[tax[l[i]]--]=i;
  for(int i=cnt;i;i--)
    {
      int x=t[i];
      if(T==0)siz[x]=1;
      else siz[fa[x]]+=siz[x];
    }
  siz[1]=0;//
  for(int i=cnt;i;i--)
    {
      int x=t[i]; sum[x]=siz[x];
      for(int j=0;j<26;j++)
    if(go[x][j])sum[x]+=sum[go[x][j]];
    }
}
void dfs(int x,int s)
{
  if(s<=siz[x])return;//<=
  s-=siz[x];
  for(int j=0;j<26;j++)
    {
      if(!go[x][j])continue;
      if(s>sum[go[x][j]])s-=sum[go[x][j]];
      else
    {
      putchar(j+'a');
      dfs(go[x][j],s);
      return;
    }
    }
}
int main()
{
  scanf("%s",s+1); int n=strlen(s+1);
  scanf("%d%d",&T,&K);
  for(int i=1;i<=n;i++)add(s[i]-'a');
  Tsort(); dfs(1,K); puts("");
  return 0;
}
原文地址:https://www.cnblogs.com/Zinn/p/10109375.html