使用后缀自动机求后缀数组

倒序建立后缀自动机的fail树就是后缀树,dfs后缀树得到后缀数组

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

  int last,dis[200001],val[200001],cnt,a[200001][26],fa[200001],sa[200001],n,sta[200001],top;
  int tr[200001][26],rank[200001],height[200001];
  char st[200001];

  void ins(int num,int pos){
      int p=last,np=last=++cnt;
      dis[np]=dis[p]+1;val[np]=pos;
      while (!a[p][num]&&p) a[p][num]=np,p=fa[p];
      if (!p) fa[np]=1;else{
        int q=a[p][num];
        if (dis[p]+1==dis[q]) fa[np]=q;else{
          int nq=++cnt;dis[nq]=dis[p]+1;
          memcpy(a[nq],a[q],sizeof(a[q]));
          fa[nq]=fa[q];
          fa[q]=fa[np]=nq;
          while (a[p][num]==q) a[p][num]=nq,p=fa[p];
      }
    }
  }

  void dfs1(int po){
    for (int i=0;i<26;i++)
      if (a[po][i]&&dis[a[po][i]]==dis[po]+1){
        sta[++top]=i;
        tr[fa[a[po][i]]][sta[dis[a[po][i]]-dis[fa[a[po][i]]]]]=a[po][i];//确定后缀树的出边
        dfs1(a[po][i]);
        top--;
      }
  }

  void dfs2(int po){
    if (val[po]) sa[++cnt]=val[po];
    for (int i=0;i<26;i++)
      if (tr[po][i])
        dfs2(tr[po][i]);
  }

  void getheight(){
      int k=0;
      int po1,po2;
      for (int i=1;i<=n;i++){
          po1=i,po2=sa[rank[i]-1];
          if (k) k--;
          while ((st[po1+k]==st[po2+k])&&(po1+k<=n)&&(po2+k<=n))
          k++;
          height[rank[i]-1]=k;
      }
  }

  int main(){
    scanf("%s",st+1);
    n=strlen(st+1);
    last=cnt=1;
    for (int i=n;i>=1;i--)
      ins(st[i]-‘a‘,i);

    dfs1(1);
    cnt=0;
    dfs2(1);
    for (int i=1;i<=n;i++) rank[sa[i]]=i;
    getheight();

    for (int i=1;i<=n;i++) printf("%d ",sa[i]);printf("\n");
    for (int i=1;i<n;i++) printf("%d ",height[i]);
  }
时间: 2024-08-06 08:29:55

使用后缀自动机求后缀数组的相关文章

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的

【Luogu3804】【模板】后缀自动机(后缀自动机)

[Luogu3804][模板]后缀自动机(后缀自动机) 题面 洛谷 题解 一个串的出现次数等于\(right/endpos\)集合的大小 而这个集合的大小等于所有\(parent\)树上儿子的大小 这样子的话,给每个终止位置的\(size\)记为\(1\) 然后按照拓扑序累加,这就是\(right/endpos\)集合的大小 最后对于每个\(size>1\)的节点,\(ans=max(longest*size)\) #include<iostream> #include<cstdi

SETI ACdream - 1430 后缀自动机求不相交子串

http://blog.csdn.net/gatevin/article/details/45875343 题目是求不重叠的不同子串个数 一般来说,endpos集合包含了子串结尾位置,每个状态都包含了若干子串.endpos集合的大小就是这些子串的出现次数 但是这样会重叠.那么可以求出endpos结合的结尾最小值,和结尾最大值. 那么长度小于mx - mi的子串,是肯定不会重叠的 至于有多少个,可以由mxcnt决定 #include <bits/stdc++.h> #define IOS ios

luogu SP8093 后缀自动机+树状数组+dfs序

这题解法很多,简单说几个: 1. 线段树合并,时间复杂度是 $O(nlog^2n)$ 的. 2. 暴力跳 $fail,$ 时间复杂度 $O(n\sqrt n),$ 比较暴力. 3. 建立后缀树后在 $dfs$ 序上数点,时间复杂度为 $O(nlogn),$ 十分优秀. Code: #include <bits/stdc++.h> #define N 200007 #define setIO(s) freopen(s".in","r",stdin) , f

[TJOI2015]弦论(后缀数组or后缀自动机)

解法一:后缀数组 听说后缀数组解第k小本质不同的子串是一个经典问题. 把后缀排好序后第i个串的本质不同的串的贡献就是\(n-sa[i]+1-LCP(i,i-1)\)然后我们累加这个贡献,看到哪一个串的时候,这个贡献的和大于等于k,然后答案就在这个串里了,然后枚举就行了. 那么第k小子串该怎么办? 我们考虑二分答案,我们按字典序大小二分一个子串(具体就是二分第k小的本质不同子串,因为这个串可以\(O(n)\)求),然后看看比这个串小的串有多少个?然后改变上下界就行了. 那么我们如何求出比一个串小的

康复计划#1 再探后缀自动机&amp;后缀树

本篇口胡写给我自己这样的东西都忘光的残废选手 以及那些刚学SAM,看了其他的一些东西并且没有完全懵逼的人 (初学者还是先去看有图的教程吧,虽然我的口胡没那么好懂,但是我觉得一些细节还是讲清楚了的) 大概是重复一些有用的想法和性质,用以加深印象吧-如果可以的话希望也能理解得更透彻一点- 1.如何设计出一个后缀自动机? 现在用的SAM并不是本来就在那里的,要比较深入地理解,就不能只从验证它对不对的角度考虑,而要考虑为什么它是这个样子. 要一个能够接受后缀的有限状态机,并不用像现在的SAM那样弄,比如

后缀自动机学习小记

简介 后缀三姐妹:后缀数组,后缀自动机,后缀树. 后缀自动机:Suffix Automation,也叫SAM. 创立算法的思路来源:能不能构出一个自动机(本质就是一个有向图),能识别一个串的所有后缀. 识别所有后缀基础想法 把所有的后缀都放进一个trie里面,比如串aabbabd. 这样的状态太多了,怎么把状态数缩小. 减小状态数的方法 定义一个子串的right集合为这个子串在原串中出现的右端点集合. 如果两个子串A和B的right集合完全相同的话,那么他们明显一个是另一个的后缀,假设A是B的后

『后缀自动机入门 SuffixAutomaton』

本文的图片材料多数来自\(\mathrm{hihocoder}\)中详尽的\(SAM\)介绍,文字总结为原创内容. 确定性有限状态自动机 DFA 首先我们要定义确定性有限状态自动机\(\mathrm{DFA}\),一个有限状态自动机可以用一个五元组\((\mathrm{S},\Sigma,\mathrm{st},\mathrm{end},\delta)\)表示,他们的含义如下: \(1.\) \(\mathrm{S}\) 代表自动机的状态集 \(2.\) \(\Sigma\) 代表字符集,也称字

【BZOJ-3998】弦论 后缀自动机

3998: [TJOI2015]弦论 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2018  Solved: 662[Submit][Status][Discuss] Description 对于一个给定长度为N的字符串,求它的第K小子串是什么. Input 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. Output 输出仅