HDU 4080 Stammering Aliens && 后缀数组

题意:给你数字m和一个字符串,问你在字符串中至少出现m次的子串的最大长度,并输出子串最后出现的首字母的位置,如果长度相同时有多种情况取最后一次最靠后出现的那个。(没考虑这个,WA了好几天。我是弱渣渣~)

解法:先求出后缀数组,然后二分长度,判断这个长度是否出现过m次,如果出现了m次 ,先判断长度是否是最大,再判断最后一个是否足够靠后。

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;

const int maxnode = 1e5+10;
char s[maxnode];
int sa[maxnode], t[maxnode], t2[maxnode], c[maxnode];
void buildsa(int n)
{
    int m = 256, *x = t, *y = t2;
    //基数排序
    for(int i = 0; i < m; ++ i) c[i] = 0;
    for(int i = 0; i < n; ++ i) c[x[i]=s[i] ]++;
    for(int i = 1; i < m; ++ i) c[i] += c[i-1];
    for(int i = n-1; i >= 0; -- i) sa[--c[x[i]]] = i;
    for(int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        for(int i = n-k; i < n; ++ i) y[p++] = i;
        for(int i = 0; i < n; ++ i) if(sa[i] >= k) y[p++] = sa[i] - k;
        //基数排序第一关键字
        for(int i = 0; i < m; ++ i) c[i] = 0;
        for(int i = 0; i < n; ++ i) c[x[y[i]]] ++;
        for(int i = 0; i < m; ++ i) c[i] += c[i-1];
        for(int i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        //
        swap(x,y);
        p = 1; x[sa[0]] = 0;
        for(int 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;
    }
}

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

int main()
{
    //freopen("in.txt","r",stdin);
    int m;
    while(~scanf("%d", &m)&&m)
    {
        scanf("%s",s);
        if(m==1)
        {
            printf("%d 0\n", strlen(s));
            continue;
        }

        int n = strlen(s) + 1;
        buildsa(n);
        getheight(n);
        int left = 0, right = n*2, mid;
        int pos = -1, len = 1;
        while(left<right-1)
        {
            mid = (right + left)/2;
            bool flag = false;
            int post = -1, numt = 1;
            for(int i = 2 ; i <= n ; ++ i)
            {
                if(height[i]>=mid)
                {
                    numt ++;
                    post = max(post, sa[i]);
                    post = max(post, sa[i-1]);
                    if(numt>=m)
                    {
                        if(len<mid || (len==mid)&&pos<post )
                        {
                            pos = post;
                            len = mid;
                        }
                        flag = true;
                    }
                }
                else
                {
                    post = -1;
                    numt = 1;
                }
            }

            if(flag)
            {
                left = mid;
            }
            else
            {
                right = mid;
            }
        }
        if(pos==-1)
        {
            printf("none\n");
        }
        else
        {
            printf("%d %d\n", len, pos);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值