后缀数组简单总结

主要参考:

http://hi.baidu.com/ahnkftravhdhkyr/item/cc38703dd46547cd392ffab1

及cxlove博客

主要是论文《后缀数组——处理字符串的有力工具》一些题解和其它题目的主要题解

新模板:UVA11107 后缀数组(new模板)

一般的模板:

#include<functional>  
#include<algorithm>  
#include<iostream>  
#include<cstring>  
#include<cstdio>  
#include<vector>  
#include<queue>  
#include<cmath>  
#include<map>  
#include<set>  
#define REP(i, n) for(int i=0; i<n; i++)  
#define CLR(a, b) memset(a, b, sizeof(a))  
#define PB push_back  
#define LL long long  
using namespace std;  
  
const int MAXN = 1010 * 100;  
const int INF = 0x3f3f3f3f;  
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];  
char r[MAXN];  
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];  

int cmp(int *r, int a,int b, int k)  
{  
    return r[a] == r[b] && r[a + k] == r[b + k];  
}  
void build_sa(int *r, int *sa, int n,int m)  
{  
   int i,j, p;  
   int *x = wa, *y = wb, *t;  
   for (int i= 0; i < m; i++) wn[i] = 0;  
   for (int i= 0; i < n; i++) wn[x[i] = r[i]]++;  
   for (int i = 1; i < m; i++) wn[i] += wn[i - 1];  
   for (int i = n - 1; i >= 0; i--) sa[--wn[x[i]]] = i;  
   for (p = 1, j = 1; p < n; j <<= 1, m = p)  
   {  
       for (p = 0, i = n - j; i < n; i++) y[p++] = i;  
       for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;  
  
       for (i = 0; i < m; i++) wn[i] = 0;  
       for (i = 0; i < n; i++) wn[wv[i] = x[y[i]]]++;  
       for (i = 1; i < m; i++) wn[i] += wn[i - 1];  
       for (i = n - 1; i >= 0; i--) sa[--wn[wv[i]]] = y[i];  
  
       t = x; x = y; y = t;  
       x[sa[0]] = 0; p = 1;  
       for (i = 1; i < n; i++)  
            x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;  
   }  
}  
void getHeight(int *r, int *sa, int n)  
{  
    int i, j, k = 0;  
    for (i = 1; i <= n; i++)  
    {  
        rank[sa[i]] = i;  
        height[i] = 0;  
    }  
    for (i = 0;i < n; i++)  
    {  
        if (k) k--;  
        j = sa[rank[i] - 1];  
        while (r[i + k] == r[j + k]) k++;  
        height[rank[i]] = k;  
    }  
}  


debuge:

debuge:
0.debuge
void debuge_sa()
{
    for (int i = 0; i <= n; i++)
    cout << i << ' ' << sa[i] << ' ' << height[i] << endl;
    cout << "^^^^^^^^^^^^^^" << endl << endl;

    for (int i = 0; i <= n; i++)
    cout << i << ' ' << rank[i] << endl;
    cout << "^^^^^^^^^^^^^^" << endl << endl;

    init_rmq(n);
    for (int i = 1; i <= n; i++)
    {
        for (int j = i; j <= n; j++)
            printf("%d ", rmq(i, j));
        printf("\n");
    }
}
/*
debuge_sa:
ABABABAB
0 8 0
1 6 0
2 4 2
3 2 4
4 0 6
5 7 0
6 5 1
7 3 3
8 1 5
^^^^^^^^^^^^^^


0 4
1 8
2 3
3 7
4 2
5 6
6 1
7 5
8 0
^^^^^^^^^^^^^^


0 2 2 2 0 0 0 0
2 4 4 0 0 0 0
4 6 0 0 0 0
0 0 0 0 0
0 1 1 1
1 3 3
3 5
0
*/


一些题目:

一些论文上的题目:
//1、求单个子串的不重复子串个数。SPOJ 694
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int main()
{
    int t,i;
    int n, m;
    cin >> t;
    while (t--)
    {
        RS(r);
        n = strlen(r);
        m = 256;
        REP(i, n)
            a[i] = r[i];
        a[n] = 0;
        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);
        int ans = 0;
        FE(i, 1, n) ans += n - sa[i] - height[i];
        cout << ans << endl;
    }
    return 0;
}
/**
4、最长重复不重叠子串 PKU1743
题意:给出一个旋律,用n个数字[1,88]表示其音符,问它最长的主题长度是多少。
一个旋律的主题是一段至少出现过两次的不重叠音乐片段。
所谓重复出现,包括一段音乐全体加上某个数后再次出现。
如1 2 3 4 5和5 6 7 8 9是同一个音乐片段。主题长度至少为5.无解输出0。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
bool check(int L, int n)
{
    int smin = sa[1];
    int smax = sa[1];
    for (int i = 2; i <= n; i++)
    {
        if (height[i] < L)
        {
            if (smax - smin >= L) return 1;
            smin = smax = sa[i];
        }
        else
        {
            smin = min(smin, sa[i]);
            smax = max(smax, sa[i]);
        }
    }
//    if (height[n] >= L && smax - smin >= L)///
//        return 1;
    return 0;
}

void solve(int n)
{
    int l = 0;
    int r = n / 2;
    while (l <= r)
    {
        int m = (l + r) >> 1;
        if (check(m, n)) l = m + 1;
        else r = m - 1;
    }
    WI(l);
}
int x[MAXN];
int main()
{
    int t,i;
    int n, m;
    while (~RI(n) && n)
    {
        REP(i, n) RI(x[i]);
        a[0] = 500;
        for (int i = 1; i < n; i++) a[i] = x[i] - x[i - 1] + 100;
        a[n] = 0;
        m = 501;
        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);

        solve(n);
    }
    return 0;
}
/**
5、最长的出现k次的重复(可重叠)子串。 PKU3261
题目大意:给出n个数字组成的一个字符串,求最长的恰好出现k次的重复子串(可重叠)的字符串的长度。
分析:后缀数组一个经典的应用。先二分答案,然后分组。只要某一组包含的后缀数量大于等于k,表示有解。这个不难理解。等完成了论文里面的练习之后,我再写个总结笔记吧。
深刻体会到后缀数组的强大....
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
char r[MAXN];
int n;

int K;
bool check(int k)
{
    int num = 1;
    FE(i, 2, n)
    {
        if (height[i] < k)
        {
            if (num >= K) return 1;
            num = 1;
        }
        else
            num++;
    }
    if (num >= k) return 1;
    return 0;
}

void solve()
{
    int l = 0, r = n, mid;
    while (l <= r)
    {
        mid = (l + r) >> 1;
        if (check(mid)) l = mid + 1;
        else r = mid - 1;
    }
    cout << r << endl;
}

map<int, int>M;
int main()
{
    int x[MAXN];
    int T;
    while (~RII(n, K) && n)
    {
        M.clear();
        int MM = 1;
        REP(i, n)
        {
            RI(x[i]);
            if (M.count(x[i]) == 0) M[x[i]] = MM++;
        }
        REP(i, n) a[i] = M[x[i]];
        a[n]= 0;
        build_sa(a, sa, n + 1, MM + 5);
        getHeight(a, sa, n);
        solve();
    }
}
/***
7.求一个串最多由哪个串复制若干次得到 PKU2406 (!!!此代码是网上的)
题目大意:给出一个字符串s,则存在子串a,a重复k次后得到s。求k的最大值。
*/
#include<stdio.h>
#include<cstring>
using namespace std;
const int maxn=3000010;
int ws[maxn],wa[maxn],wb[maxn],wv[maxn],sa[maxn],rank[maxn],height[maxn],a[maxn],f[maxn];
char s[maxn];
//dc3
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int c0(int *r,int a,int b)
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
int c12(int k,int *r,int a,int b)
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
void sort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i=0;i<n;i++) wv[i]=r[a[i]];
    for(i=0;i<m;i++) ws[i]=0;
    for(i=0;i<n;i++) ws[wv[i]]++;
    for(i=1;i<m;i++) ws[i]+=ws[i-1];
    for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
    return;
}
void dc3(int *r,int *sa,int n,int m)
{
     int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
     r[n]=r[n+1]=0;
     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
     sort(r+2,wa,wb,tbc,m);
     sort(r+1,wb,wa,tbc,m);
     sort(r,wa,wb,tbc,m);
     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
     rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
     if(p<tbc) dc3(rn,san,tbc,p);
     else for(i=0;i<tbc;i++) san[rn[i]]=i;
     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
     if(n%3==1) wb[ta++]=n-1;
     sort(r,wb,wa,ta,m);
     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
     for(i=0,j=0,p=0;i<ta && j<tbc;p++)
     sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
     for(;i<ta;p++) sa[p]=wa[i++];
     for(;j<tbc;p++) sa[p]=wb[j++];
     return;
}
void cal(int *r,int *sa,int n){
     int i,j,k=0;
     for (i=1;i<=n;i++) rank[sa[i]]=i;
     for (i=0;i<n;height[rank[i++]]=k)
         for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
     return;
}
void rmq(int *height,int n){
     int i,j=rank[0],min=n-sa[j];
     for (i=j;i>=1;i--){
         f[i]=min;
         min=min<height[i]?min:height[i];
     }
     for (i=j+1,min=1000000;i<=n;i++){
         min=min<height[i]?min:height[i];
         f[i]=min;
     }
     return;
}
int work(int n){
    int i;
    for (i=0;i<n;i++)
        if (n%(i+1)==0)
           if (f[rank[i+1]]==n-i-1) return n/(i+1);
    return 0;
}
int main(){
    while (scanf("%s",s)!=EOF){
          if (strcmp(s,".")==0) return 0;
          int n=strlen(s),i;
          for (i=0;i<n;i++) a[i]=static_cast<int>(s[i]);
          a[n]=0;
          dc3(a,sa,n+1,123);
          cal(a,sa,n);
          rmq(height,n);
          printf("%d\n",work(n));
          }
    return 0;
}
/***
8.最长公共子串 Pku2774--Long Long Message
题目大意:给出两个字符串,求它们的最长公共子串的长度。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
char r1[MAXN], r2[MAXN];
int nr1, nr2;

bool check(int x, int y)
{
    if (x > y) swap(x, y);
    if (x < nr1 && y > nr1) return 1;
    else return 0;
}

void solve()
{
    int ans = 0;
    FE(i, 2, nr1 + nr2 + 1)
    {
        if (check(sa[i], sa[i - 1]))
        {
            if (height[i] > ans) ans = height[i];
        }
    }
    cout << ans << endl;
}

int main()
{
    while (scanf("%s%s", r1, r2) != EOF)
    {
        nr1 = strlen(r1);
        nr2 = strlen(r2);
        REP(i, nr1) a[i] = (int)r1[i];
        a[nr1] = 1;
        REP(i, nr2) a[i + nr1 + 1] = (int)r2[i];
        a[nr1 + nr2 + 2] = 0;
        build_sa(a, sa, nr1 + nr2 + 2, 256);
        getHeight(a, sa, nr1 + nr2);
        solve();
    }

}
/**
9.重复次数最多的重复子串  SPOJ687--Repeats
题目大意:给出一个字符串,求该字符串中出现次数最多的连续重复子串。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int h[MAXN][20];

int init_rmq(int n)
{
    FE(i, 1, n)
        h[i][0] = height[i];
    for (int j = 1; (1 << j) <= n; j++)
    {
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
        {
            h[i][j] = min(h[i][j - 1], h[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int rmq(int L, int R)///要求L!=R 且 L>=0 , R >= 0
{
    L = rank[L];
    R = rank[R];
    if (L > R) swap(L, R);
    L++;
    int k = 0;
    while ((1 << (k + 1)) <= (R - L + 1)) k++;
    return min(h[L][k], h[R - (1 << k) + 1][k]);
}

int getnum(int L, int n)
{
    int ans = 1;
    for (int i = 0; i < n - L; i += L)
    {
        int dd = rmq(i, i + L);
        if (i && dd % L)
        {
            dd = L - (dd % L);
            dd = rmq(i - dd, i + L - dd);
        }
        ans = max(ans, dd / L + 1);
    }
    return ans;
}

void solve(int n)
{
    int ans = 1;
    FE(i, 1, n - 1)
    {
        ans = max(ans, getnum(i, n));
    }
    WI(ans);
}
int main()
{
    int t,i;
    int n, m;
    int T;
    RI(T);
    while (T--)
    {
        RI(n);
        REP(i, n) scanf(" %c", &r[i]);
        REP(i, n ) a[i] = r[i];
        a[n] = 0;
        m = 256;
        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);
        init_rmq(n);
        solve(n);
    }
    return 0;
}
/**
 10.多个串的公共子串问题 PKU3294
[后缀数组]Pku3294--Life Forms
题目大意:给出n个字符串,求一个最长的串,它出现在一半的串以上。若要多个,按字典顺序输出。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
//int h[MAXN][30];
int idx[MAXN];

inline int getid(int x)
{
    return idx[sa[x]];
}

bool check(int k, int t, int n)
{
    int vis[110];///!!!
    CLR(vis, 0);
    int num = (t >> 1) + 1;

    int step = 1;///
    int x = getid(1);///
    vis[x] = 1;///
    int tol = 1;///

    FE(i, 2, n)
    {
        if (height[i] < k)
        {
            if (tol >= num) return 1;
            step++;
            x = getid(i);
            vis[x] = step;
            tol = 1;
        }
        else
        {
            x = getid(i);
            if (vis[x] != step)
            {
                vis[x] = step;
                tol++;
            }
        }
    }
    if (tol >= num && height[n - 1] >= k) return 1;
    return 0;
}

void print(int k, int t, int n)
{
    int vis[110];///!!!
    CLR(vis, 0);
    int num = (t >> 1) + 1;

    int step = 1;///
    int x = idx[sa[1]];///
    vis[x] = 1;///
    int tol = 1;///

    FE(i, 2, n)
    {
        if (height[i] < k)
        {
            if (tol >= num)
            {
                int x = sa[i - 1];///!!!
                for (int j = x; j < x + k; j++)
                    printf("%c", (char)a[j]);
                printf("\n");
            }
            step++;
            x = idx[sa[i]];
            vis[x] = step;
            tol = 1;
        }
        else
        {
            x =idx[sa[i]];
            if (vis[x] != step)
            {
                vis[x] = step;
                tol++;
            }
        }
    }
    if (tol >= num && height[n - 1] >= k)///!!!
    {
        int x = sa[n - 1];
        for (int j = x; j < x + k; j++)
            printf("%c", (char)a[j]);
        printf("\n");
    }
}

void solve(int t, int n)
{
    int l = 0, r = 1010, mid;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if (check(mid, t, n)) l = mid + 1;
        else r = mid - 1;
    }
    if (r == 0) printf("?\n");
    else print(r, t, n);
    printf("\n");
}

int main()
{
    int t,i;
    int l, n, m;
    int ncase = 1;
    while (cin >> t && t)
    {
        n = 0;
        m = 256;
        CLR(idx, 0);///!!!
        FE(i, 1, t)
        {
            RS(r);
            l = strlen(r);
            REP(j, l)
            {
                a[n] = (int)r[j];
                idx[n++] = i;
            }
            a[n++] = m++;
        }
        a[--n] = 0;
        build_sa(a,sa,n+1,m);
        getHeight(a,sa,n);
        solve(t, n);
    }
    return 0;
}
/**
11、出现或反转后出现所有字符串中的最长子串 Pku1226--Substrings
题目大意:给出n个串,求一个最长的串x,它或者它的反串出现在所有的串中。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];

int getid(int x)
{
    return idx[sa[x]];
}

bool check(int l, int t, int n)
{
    int vis[110], step, tol;
    CLR(vis, 0);
    step = 1;
    int x = getid(1);
    vis[x] = step;
    tol = 1;

    FE(i, 2, n)
    {
        if (height[i] < l)
        {
            CLR(vis, 0);
            ++step;
            x = getid(i);
            vis[x] = step;
            tol = 1;
        }
        else
        {
            x = getid(i);
            if (vis[x] != step)
            {
                vis[x] = step;
                ++tol;
            }
            if (tol >= t) return 1;
        }
    }
    return 0;
}

void solve(int t, int n)
{
    int l = 0, r = 100;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid, t, n)) l = mid + 1;
        else r = mid - 1;
    }
    cout << r << endl;
}

int main()
{
    int T;
    cin >> T;
    int t, n, m;
    while (T--)
    {
        RI(t);
        CLR(idx, 0);
        n = 0;
        m = 256;
        FE(i, 1, t)///1
        {
            RS(r);
            int l = strlen(r);
            REP(j, l)
            {
                a[n] = (int)r[j];
                idx[n++] = i;
            }
            a[n++] = m++;
            for (int j = l - 1; j >= 0; j--)
            {
                a[n] = (int)r[j];
                idx[n++] = i;
            }
            a[n++] = m++;
        }
        a[--n] = 0; --m;
        if (t == 1)
        {
            cout << strlen(r) << endl;
            continue;
        }
        build_sa(a, sa, n + 1, m);
        get_height(a, sa, n);
        solve(t, n);
    }
    return 0;
}
/**
 12、不重叠地至少两次出现在所有字符串中的最长子串 spoj220
题目大意:给出n个字符串,求一个最长的串x,x在每个字符串中不重叠出现至少两次。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];

int ns;
int n, m;
int lmax;
int used[12];
int smin[12], smax[12];

int getid(int x)
{
    return idx[sa[x]];
}
void clear_up()
{
    REP(i, 12) smin[i] = MAXN;
    REP(i, 12) smax[i] = -1;
}

bool check(int L)
{
    clear_up();

    smin[getid(1)] = sa[1];
    smax[getid(1)] = sa[1];

    FE(i, 2, n)
    {
        int x = getid(i);
        int y = sa[i];
        if (height[i] < L)
        {
            int tol = 0;
            REP(i, ns)
                if (smax[i] - smin[i] >= L) tol++;
            if (tol == ns) return 1;
            clear_up();
            smin[x] = smax[x] = y;
        }
        else
        {
            smin[x] = min(smin[x], y);
            smax[x] = max(smax[x], y);
        }
    }
    if (height[n] >= L)
    {
        int tol = 0;
        REP(i, ns)
            if (smax[i] - smin[i] >= L) tol++;
        if (tol == ns) return 1;
    }
    return 0;
}

void solve()
{
    int l = 0;
    int r = lmax;
    while (l <= r)
    {
        int m = (l + r) >> 1;
        if (check(m)) l = m + 1;
        else r = m - 1;
    }
    WI(r);
}

int main()
{
    int T;
    RI(T);
    while (T--)
    {
        RI(ns);
        CLR(idx, 0);
        m = 256;
        n = 0;
        lmax = -1;
        REP(i, ns)
        {
            RS(r);
            int l = strlen(r);
            if (lmax == -1 || lmax > l) lmax = l;
            REP(j, l)
            {
                a[n] = r[j];
                idx[n++] = i;
            }
            a[n++] = m++;
//            idx[n++] = 0;
        }
        a[--n] = 0;

        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);

        solve();
    }
    return 0;
}
/**
13.两个字符串的重复子串个数。 Pku3415--Common Substrings
题目大意:给出两个字符串,求在这两个字符串中出现次数不少于k的公共子串的个数(可以重复)。
例如xx和xx这两个字符串,假如k=1,则有5个。(4个位置不同的“x”,和一个“xx”)。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
int h[MAXN][30];

int n1, n2, n, m, k;

int getid(int x)
{
    return idx[sa[x]];
}
stack<pair<int, int> > S;

LL getnum(int l, int r, int op)
{
    LL ret = 0;
    LL now = 0;
    while (!S.empty()) S.pop();

    FE(i, l, r - 1)
    {
        int c = getid(i);

        if (c == op) ret += now;
        else now += height[i + 1] - k + 1;

        int xn = (c == op ? 0 : 1);
        int xs = 0;
        while (!S.empty() && S.top().first >= height[i + 1])
        {
            xn += S.top().second;
            now -= 1LL * S.top().second * (S.top().first - height[i + 1]);
            S.pop();
        }
        if (xn) S.push(make_pair(height[i + 1], xn));
    }
    if (getid(r) == op) ret += now;

    return ret;
}

void solve()
{
    LL ans = 0;
    LL last = 1;

    FE(i, 2, n)
    {
        if (height[i] < k)
        {
            ans += getnum(last, i - 1, 1) + getnum(last, i - 1, 2);
            last = i;
        }
    }
    if (height[n] >= k) ans += getnum(last, n, 1) + getnum(last, n, 2);

    cout << ans << endl;
}

int main()
{
    int x;
    while (cin >> k && k)
    {
        CLR(idx, 0);
        n = 0;
        m = 256;

        RS(r);
        n1 = strlen(r);
        REP(i, n1)
        {
            a[n] = r[i];
            idx[n++] = 1;
        }

        a[n++] = m++;

        RS(r);
        n2 = strlen(r);
        REP(i, n2)
        {
            a[n] = r[i];
            idx[n++] = 2;
        }

        a[n] = 0;

        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);

        solve();
    }
    return 0;
}
一些其他题目:
/***
输出n个串中,每个串不属于于其它串的最小子串 URAL 1713 Key Substrings 后缀数组
*/

const int MAXN = 1010 * 100;

int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];

inline int getid(int x) { return idx[sa[x]]; }

int n, m;
int ll[MAXN], rr[MAXN];///left记录sa中i位置的左边第一个不是同一串的后缀之间的lcp,rr同理。没有则值为0
void pre()
{
    CLR(ll, 0); CLR(rr, 0);
    for (int i = 2; i <= n; i++)
    {
        if (getid(i) != getid(i - 1)) ll[i] = height[i];
        else ll[i] = min(ll[i - 1], height[i]);
    }
    for (int i = n - 1; i >= 1; i--)
    {
        if (getid(i) != getid(i + 1)) rr[i] = height[i + 1];
        else rr[i] = min(rr[i + 1], height[i + 1]);
    }
}
void solve()
{
    pre();
    int p[2];
    p[0] = INF; p[1] = 0;
    for (int i = 0; i <= n; i++)
    {
        if (idx[i])
        {
            int x = max(ll[rank[i]], rr[rank[i]]); x++;
            if (idx[i + x - 1] == idx[i] && p[0] > x) p[0] = x, p[1] = i;///判断是否超过长度
        }
        else
        {
            for (int j = p[1]; j < p[1] + p[0]; j++)
                printf("%c", (char)a[j]);
            printf("\n");
            p[0] = INF; p[1] = 0;
        }
    }
}
int main()
{
    int t, i, l;
    while (~scanf("%d", &t))
    {
        n = 0;
        m = 256;
        CLR(idx, 0);
        for(int i = 1; i <= t; i++)
        {
            scanf("%s", r);
            l = strlen(r);
            REP(j, l)
            {
                a[n] = (int)r[j];
                idx[n++] = i;
            }
            a[n++] = m++;
        }
        a[--n] = 0;
        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);
        solve1();
    }
    return 0;
}
/***
题目:给出一个A串,给出若干个B串,问A串中有多少个不同的子串不是B中的子串 hdu4416Good Article Good sentence
将所有的串拼接在一起,中间用一个不同的字符分隔开。然后求一次后缀数组以及height数组。
然后对于A中的某一个后缀,统计一下有B中的LCA有多少,就OK了,说明有A的这个后缀有LCA个子串在B中出现过。
只需要从前往后以及从后往前统计一次height就OK了。注意我们这里统计的是A与B的LCA。如果连续的两个sa是A中,那我们需要求一次最小值,保证求的是和B串的LCA。
但是题目要求的是A中的不同的子串,所以还要去重,遍历一次,如果连续两个都是A串的,则更新一下
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int ncase;
int n, m;
int L;
int pos[MAXN];
void init()
{
    CLR(pos, 0);
    ///从前向后
    int tmp = INF;
    for (int i = 1; i <= n; i++)
        if (sa[i] < L)
        {
            tmp = min(tmp, height[i]);
            pos[sa[i]] = max(pos[sa[i]], tmp);
        }
        else tmp = INF;
    ///从后向前
    tmp = INF;
    for (int i = n; i >= 1; i--)
        if (sa[i - 1] < L)
        {
            tmp = min(tmp, height[i]);
            pos[sa[i - 1]] = max(pos[sa[i - 1]], tmp);
        }
        else tmp = INF;
}

void solve()
{
    init();
    for(int i=1;i<=n;i++)
         if(sa[i]<L&& sa[i-1]<L)
            if(pos[sa[i-1]]<height[i])
                pos[sa[i-1]]=height[i];
    LL ans = (LL) L * (L + 1) / 2;
    for (int i = 0; i < L; i++) ans -= pos[i];
    printf("Case %d: %I64d\n", ncase++ , ans);

}

int main()
{
    int T;
    int n2;
    RI(T);
    ncase = 1;
    while (T--)
    {
        RI(n2);
        RS(r);
        L = strlen(r);
        m = 256;
        n = 0;
        REP(i, L) a[n++] = r[i];
        a[n++] = m++;

        REP(i, n2)
        {
            RS(r);
            int l = strlen(r);
            REP(j, l)
                a[n++] = r[j];
            a[n++] = m++;
        }
        a[--n] = 0;
        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);

        solve();
    }
    return 0;
}
/**
题目:给出两个串,找出两个串的后缀的LCP恰好为K的有多少对。POJ 3729 Facer’s string(后缀数组)
将两个串拼接后,求出后缀数组
由于题目要求两个后缀的LCP恰好为K。
可以求出大于等于K的减去大于等于K+1的有多少个。
利用height将后缀分组,然后统计每一组内第一个串和第二个串分别有多少
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];

int n1, n2, n, m, k;

int getid(int x)
{
    return idx[sa[x]];
}

LL get(int L)
{
    int d[3];
    CLR(d, 0);
    d[getid(1)] = 1;
    LL ans = 0;
    FE(i, 2, n)
    {
        if (height[i] < L)
        {
            if (d[2] > 0)
            ans += 1LL * d[1];
            CLR(d, 0);
            d[getid(i)] = 1;
        }
        else
            d[getid(i)]++;
    }
    if (height[n] >= L)
        ans += 1LL * d[1] * d[2];
    return ans;
}

void solve()
{
    cout << get(k) - get(k + 1) << endl;
}

int main()
{
    int x;
    while (cin >> n1 >> n2 >> k)
    {
        CLR(idx, 0);
        n = 0;
        m = 10003;
        REP(i, n1)
        {
            RI(x);
            a[n] = x + 1;
            idx[n++] = 1;
        }
        a[n++] = m++;
        REP(i, n2)
        {
            RI(x);
            a[n] = x + 1;
            idx[n++] = 2;
        }
        a[n] = 0;
        if (n == 0 || m == 0)
        {
            cout << 0 << endl;
            continue;
        }

        build_sa(a, sa, n + 1, m);
        getHeight(a, sa, n);

        solve();
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值