后缀数组及应用

  • First things first

    这玩意似乎我17年搞过,但是现在毫无记忆qwq

    用处是对后缀排序,和求LCS

  • 正文

    倍增法很好理解,运用基数排序,每次排序一倍长度的前缀

    qwq

    这点东西背板子比较好

    详细解释留坑

    Code (By Adscn in 2017) 因为是17年的所以非常naive

#include<bits/stdc++.h>
using namespace std;
#define File(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
#define T int
T get()
{
  T xi=0;
  bool f=0;
  char ch=getchar();
  while(ch<'0'||ch>'9')
    {
      if(ch=='-')f=1;
      ch=getchar();
    }
  while(ch>='0'&&ch<='9')xi=xi*10+ch-48,ch=getchar();
  if(f)xi=-xi;
  return xi;
}
void put(T xi)
{
  if(xi<0)putchar('-'),xi=-xi;
  char p[sizeof(T)*2+1];
  int cnt=0;
  do p[cnt++]=xi%10+48,xi/=10; while(xi);
  do putchar(p[--cnt]); while(cnt);
}
const int Size=1000001;
struct SuffixArray{
  char s[Size];
  int rank[Size],count[Size],sa[Size],rankx[Size],ranky[Size],height[Size],n;
  void getrank()
  {
    int m=150;
    n=strlen(s);
    int *key1=rankx,*key2=ranky;
    for(int i=0;i<=m;i++)count[i]=0;
    for(int i=0;i<n;i++)count[key1[i]=s[i]]++;
    for(int i=1;i<m;i++)count[i]+=count[i-1];
    for(int i=n-1;i>=0;i--)sa[--count[key1[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
      int p=0;
      for(int i=n-k;i<n;i++)key2[p++]=i;
      for(int i=0;i<n;i++)if(sa[i]>=k)key2[p++]=sa[i]-k;
      for(int i=0;i<=m;i++)count[i]=0;
      for(int i=0;i<n;i++)count[key1[key2[i]]]++;
      for(int i=1;i<m;i++)count[i]+=count[i-1];
      for(int i=n-1;i>=0;i--)sa[--count[key1[key2[i]]]]=key2[i];
      swap(key1,key2);
      p=1,key1[sa[0]]=0;
      for(int i=1;i<n;i++)key1[sa[i]]=(key2[sa[i-1]]==key2[sa[i]]&&key2[sa[i-1]+k]==key2[sa[i]+k])?p-1:p++;
      if(p>=n)break;
      m=p;
    }
    for(int i=0;i<n;i++)rank[sa[i]]=i;
  }
  void getheight()
  {
    n=strlen(s);
    int k=0;
    for(int i=0;i<n;i++)
      {
        k?k--:0;
        int p=sa[rank[k]-1];
        while(s[i+k]==s[p+k]&&(++k));
        height[rank[i]]=k;
      }
  }
};
SuffixArray k;
int main()
{
#ifndef ONLINE_JUDGE
  //File("");
#endif
  cin>>k.s;
  k.getrank();
  for(int i=0;i<k.n;i++)printf("%d ",k.sa[i]+1);
  return 0;
}
  • 应用

    先拿Anderson的用用

    Problem1

    有一个字符串s,求它的子串中至少出现过两次的最长的子串。

    Solution1

    考虑height的定义:两个rank值相近的字符串的prefix,那么很显然这样子一定比rank值远一些的更优啊!
    所以答案就是\(\max(height_i)(i∈(1,n))\)

    Problem2

    有一个字符串s,求它的子串中至少出现过两次的最长的子串(不可重叠)。

    Solution2

    二分答案然后分成很多个集合就可以了。

    Problem3

    给定一个字符串s,求它不同的子串的个数。

    Solution3

    考虑一下每一个后缀可以提供\(len−len1\)个子串,然后考虑有\(height_i\)个重复了。
    直接加起来就好了

猜你喜欢

转载自www.cnblogs.com/LLCSBlog/p/10202912.html