UVALive - 4513 Stammering Aliens(后缀数组模板)

             后缀数组+二分。二分公共长度L,然后O(n)判定就可以了。。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define MP make_pair
#define LL long long
#define CLR(a, b) memset(a, b, sizeof(a))

using namespace std;

const int maxn = 44000;
const int INF = 0x3f3f3f3f;

struct SuffixArray
{
    int s[maxn];      // 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
    int sa[maxn];     // 后缀数组
    int rank[maxn];   // 名次数组. rank[0]一定是n-1,即最后一个字符
    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 = 1; 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;
        }
    }
};

SuffixArray sa;
char ch[maxn];

int check(int mid,int m)
{
    int ret = -1, cnt = 0, tmp = -1;
    for(int i = 1; i < sa.n; i ++)
    {
        if(sa.height[i] < mid) cnt = 1, tmp = sa.sa[i];
        else cnt ++, tmp = max(tmp, sa.sa[i]);
        if(cnt >= m) ret = max(ret, tmp);
    }
    return ret;
}

void debug()
{
    for(int i = 1; i < sa.n ; i ++)
    {
        printf("%d ", sa.sa[i]);
        printf("%d\n", sa.height[i]);
    }puts("");
}

int main()
{
    int m;
    while(scanf("%d", &m), m)
    {
        scanf("%s", ch);
        int len = strlen(ch);
        sa.clear();
        sa.n = len + 1;
        if(m == 1)
        {
            printf("%d 0\n", len);
            continue;
        }
        for(int i = 0; i < len; i ++)
        {
            sa.s[i] = ch[i] - 'a' + 1;
        }
        sa.s[len] = 0;
        sa.build_sa(30);
        sa.build_height();
//        debug();
        int l = 1, r = len, ans;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            int tmp = check(mid, m);
            if(tmp != -1) l = mid + 1, ans = tmp;
            else r = mid - 1;
        }
        if(r >= 1) printf("%d %d\n", r, ans);
        else puts("none");
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值