1. 程式人生 > >字尾陣列及應用

字尾陣列及應用

  • 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\)

    個重複了。
    直接加起來就好了