UVA11107 后缀数组(new模板)

以前的模板和题目

白书例题,基本模板,多个串连接时的处理,二分加分组的处理。

注意n和m的值

n为字符个数

m为最大字符值+1

分组时,分出来的区间是[ ),左闭右开

idx[],的有效只是1-t-1,为t表示自己添加的字符

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

const int maxn = 1001 * 100 + 10;

struct SuffixArray {
  int s[maxn];      /// 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
  int sa[maxn];     // 后缀数组,sa[0]一定是n-1,即最后一个字符
  int rank[maxn];   // 名次数组
  int height[maxn]; // height数组
  int t[maxn], t2[maxn], c[maxn]; // 辅助数组
  int n; // 字符个数

  void clear() { n = 0; memset(sa, 0, sizeof(sa)); }

  /// m为最大字符值加1。!!! 调用之前需设置好s和n
  void build_sa(int m) {
    int i, *x = t, *y = t2;
    for(i = 0; i < m; i++) c[i] = 0;
    for(i = 0; i < n; i++) c[x[i] = s[i]]++;
    for(i = 1; i < m; i++) c[i] += c[i-1];
    for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
    for(int k = 1; k <= n; k <<= 1) {
      int p = 0;
      for(i = n-k; i < n; i++) y[p++] = i;
      for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
      for(i = 0; i < m; i++) c[i] = 0;
      for(i = 0; i < n; i++) c[x[y[i]]]++;
      for(i = 0; i < m; i++) c[i] += c[i-1];
      for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
      swap(x, y);
      p = 1; x[sa[0]] = 0;
      for(i = 1; i < n; i++)
        x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++;
      if(p >= n) break;
      m = p;
    }
  }

  void build_height() {
    int i, j, k = 0;
    for(i = 0; i < n; i++) rank[sa[i]] = i;
    for(i = 0; i < n; i++) {
      if(k) k--;
      int j = sa[rank[i]-1];
      while(s[i+k] == s[j+k]) k++;
      height[rank[i]] = k;
    }
  }
};

const int maxc = 100 + 10; // 串的个数
const int maxl = 1000 + 10; // 每个串的长度

SuffixArray sa;
int n;
char word[maxl];
///0-n-1;数值为n表示自己添加的,不属于任意一个字符串
int idx[maxn];
int flg[maxc];

/// 子串[L,R) 是否符合要求
bool good(int L, int R) ///[L,R)
{
    memset(flg, 0, sizeof(flg));
    if (R - L <= n / 2) return false;
    int cnt = 0;
    for (int i = L; i < R; i++)
    {
        int x = idx[sa.sa[i]];
        if (x != n && !flg[x])
        {
            flg[x] = 1;
            cnt++;
        }
    }
    return cnt > n / 2;
}
void print_sub(int L, int R) ///[L,R)
{
  for(int i = L; i < R; i++)
    printf("%c", sa.s[i] - 1 + 'a');
  printf("\n");
}
bool print_solutions(int len, bool print)
{
    int L = 0;
    for (int i = 1; i <= sa.n; i++)
    {
        if (i == sa.n || sa.height[i] < len)/// 分组,新开一段
        {
            if (good(L, i))
            {
                if (print) print_sub(sa.sa[L], sa.sa[L] + len);
                else return true;
            }
            L = i;
        }
    }
    return false;
}

void solve(int maxlen)
{
    if (!print_solutions(1, false))
        printf("?\n");
    else
    {
        int L = 1, R = maxlen, M;
        while (L <= R)
        {
            M = (L + R) >> 1;
            if (print_solutions(M, false)) L = M + 1;
            else R = M - 1;
        }
        print_solutions(R, true);
    }
}

// 给字符串加上一个字符,属于字符串i
void add(int ch, int i) {
  idx[sa.n] = i;
  sa.s[sa.n++] = ch;///改变sa.n的值
}

int main() {
    int ncase = 0;
    while (cin >> n && n)
    {
        if (ncase) puts(""); ncase++;
        int maxlen = 0;

        sa.clear();
        int m = 30;///比最大值要大1
        for (int i = 0; i < n; i++)
        {
            scanf("%s", word);
            int sz = strlen(word);
            maxlen = max(sz, maxlen);
            for (int j = 0; j < sz; j++)
                add(word[j] - 'a' + 1, i);
            add(m + i, n);// 结束字符
        }
        add(0, n);

        if (n == 1) printf("%s\n", word);
        else
        {
            sa.build_sa(m + n);
            sa.build_height();
            solve(maxlen);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值