HDU 4080 Stammering Aliens && 后缀数组

来源:互联网 发布:域名指向ip端口 编辑:程序博客网 时间:2024/06/05 11:30

题意:给你数字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;}


原创粉丝点击