HDU 5008西安网络赛B题:后缀数组求第k小子串

思路:尼玛,这题搞了一天了,比赛的时候用了n^2的方法绝对T了,然后今天看别人代码看了一天才知道。后面感觉也挺容易的,就是没想到,之前做过SPOJ 694 705求过不同子串了,知道怎么求不同子串个数了,但是比赛的时候这个技巧竟然抛在脑后了,然后就不会了。

但是今天自己用了自己的两个后缀数组的模板(倍增和DC3)的都WA了,搞得自己真想跳楼去了!!

到现在都不知道到底是哪里错了,处理的方法和标准做法都一样,但是就是WA,然后用了别人的模板,再用自己的处理方法就过了,怀疑自己的两个模板是不是哪里错了,但是之前已经用这两个模板解决无数题了,真不知道哪里还有错的,fuck……无语……

每个后缀产生的不同子串个数就是:n-sa[i]-height[i],这个在SPOJ 694 705中解决过,不再说了。

然后第k小的肯定是从height[i]数组从前往后的,因为后缀已经按字典序排好了嘛,然后找第k个就行了。

现在找到的这个模板也挺快的,而且代码量还真短,速度又快,挺不错的……这个代码排在rank1了,328ms。

其实这题是随机数据,如果不是随机数据的话,我这代码是会T的,因为在倒数第九句那个是用while循环写的,试想如果数据是10^5个a的话,那个这个处理方法就每次都遍历到最后了,就是N^2的复杂度了,所以……正确的姿势应该是RMQ+二分,官方题解也是RMQ+二分,自己理解没错。本来刚开始用自己模板的时候我就是RMQ+二分做的,但是可能是模板原因WA了,然后就用的这个模板就再懒得写RMQ+二分了,因为网络赛数据太水了,比如这场网络赛的第三题,ACdream群上有个代码,数据水到这个代码连样例都没过都A了,真不知道数据啥样的。因为这种都是随机数据,所以YY咯。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int N=111000;
char s[N]; // N>256
int n, sa[N], height[N], _rank[N], tmp[N], top[N];
void makesa() //O(N*logN)
{
    int i, j, len, na;
    na = (n < 256 ? 256 : n);
    memset(top, 0, na * sizeof(int));
    for (i = 0; i < n ; i++) top[ _rank[i] = s[i] & 0xff ]++;
    for (i = 1; i < na; i++) top[i] += top[i - 1];
    for (i = 0; i < n ; i++) sa[ --top[ _rank[i] ] ] = i;
    for (len = 1; len < n; len <<= 1)
    {
        for (i = 0; i < n; i++)
        {
            j = sa[i] - len;
            if (j < 0) j += n;
            tmp[ top[ _rank[j] ]++ ] = j;
        }
        sa[ tmp[ top[0] = 0 ] ] = j = 0;
        for (i = 1; i < n; i++)
        {
            if (_rank[ tmp[i] ] != _rank[ tmp[i-1] ] ||
                    _rank[ tmp[i]+len ]!=_rank[ tmp[i-1]+len ])
                top[++j] = i;
            sa[ tmp[i] ] = j;
        }
        memcpy(_rank, sa , n * sizeof(int));
        memcpy(sa , tmp, n * sizeof(int));
        if (j >= n - 1) break;
    }
}
void lcp() //O(4*N)
{
    int i, j, k;
    for (j = _rank[height[i=k=0]=0]; i < n - 1; i++, k++)
        while (k >= 0 && s[i] != s[ sa[j-1] + k ])
            height[j] = (k--), j = _rank[ sa[j] + 1 ];
}
ll f[N];
int main()
{
    while( scanf("%s", s)!=EOF )
    {
        n = strlen(s)+1;
        int len = n-1;
        makesa();
        lcp();
        int q;
        for(int i=1; i<=len; i++)
            f[i]=f[i-1]+len-sa[i]-height[i];
        ll k,v,l=0,r=0;
        scanf("%d",&q);
        while(q--)
        {
            scanf("%I64d",&v);
            k=(v^l^r)+1;
            if(f[len]<k)
            {
                l=r=0;
                puts("0 0");
                continue;
            }
            int pos=lower_bound(f+1,f+n,k)-f;
            l=sa[pos];
            //int len1;
            //if(pos) len1=k-f[pos-1]+height[pos];
            //else len1=k;
            int len1=len-(f[pos]-k)-l;
            while(++pos<n&&height[pos]>=len1)//这里最好用RMQ+二分,避免十万个a的数据的情况
                if(l>sa[pos]) l=sa[pos];
            l++;
            r=l+len1-1;
            printf("%I64d %I64d\n",l,r);
        }
    }
    return 0;
}
时间: 2024-12-18 09:17:33

HDU 5008西安网络赛B题:后缀数组求第k小子串的相关文章

hdu 2985 The k-th Largest Group 树状数组求第K大

The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8353   Accepted: 2712 Description Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to g

HDU 5008 求第k小子串

本题要求第k小的distinct子串,可以根据height数组,二分出这个第k小子串所在后缀的位置信息.由于题目要求子串起始下标尽可能小.所以再在rank数组中,二分出与当前后缀LCP大于等于所求子串长度的范围.通过RMQ求出这个范围中最小的sa. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string

HDU 5008 Boring String Problem(西安网络赛B题)

HDU 5008 Boring String Problem 题目链接 思路:构造后缀数组,利用height的数组能预处理出每个字典序开始的前缀和有多少个(其实就是为了去除重复串),然后每次二分查找相应位置,然后在往前往后找一下sa[i]最小的 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int

HDU 5011 Game(西安网络赛E题)

HDU 5011 Game 题目链接 思路:其实就求一个Nim和即可,要推也不难推,和为0下一个必然是胜态,因为至少取走一个,在怎么分也达不到原来那个值了,如果是非0值,就和原来Nim一样必然可以取一堆使得变成0 代码: #include <cstdio> #include <cstring> const int N = 100005; int n; long long a, sum; int main() { while (~scanf("%d", &

hdu5442(2015长春赛区网络赛1006)后缀数组+KMP /最小表示法?

题意:给定一个由小写字母组成的长度为 n 的字符串,首尾相连,可以从任意一个字符开始,顺时针或逆时针取这个串(长度为 n),求一个字典序最大的字符串的开始字符位置和顺时针或逆时针.如果有多个字典序最大的字符串,优先选择开始位置靠前的,如果开始位置相同,优先选择顺时针. 这种字符串的问题,第一反应是后缀数组,为了达到首尾相连的目的,所以先复制了一个两倍长的该字符串,然后再将串倒置,也弄成两倍长度,得到顺逆时针的两倍长的串,并对这两个字符串分别做后缀数组,得到 sa 数组(该串字典序第几小的后缀的开

2015长春网络赛 1006(后缀数组或者最小表示法)

给一个字符串,这个字符串是首位连起来的,要我们输出从哪个位置开始,顺时针走,还是你时针走,字典序最大 如果字典序最大的字符串有多个,开始的下标越小越好,如果开始的下标又相同,那么顺时针的优先. 原字符串为abab,那么只要在后面加上原字符串,变成abababab#,#是一个很小的字符, 然后进行后缀数组,sa[n-1]就是顺指针字典序最大的下标,n为abababab#的长度 逆时针,只要将字符串倒过来,[email protected],@是一个很大的字符, 然后进行后缀数组, 那么只要遍历ra

ACM-ICPC北京赛区(2017)网络赛2【后缀数组+Java//不会】

#1579 : Reverse Suffix Array 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There is a strong data structure called "Suffix Array" which can effectively solve string problems. Let S=s1s2...sn be a string and let S[i,j] denote the substring of S ranging f

HDU 5009 Paint Pearls(西安网络赛C题) dp+离散化+优化

转自:http://blog.csdn.net/accelerator_/article/details/39271751 吐血ac... 11668627 2014-09-16 22:15:24 Accepted 5009 1265MS 1980K 2290 B G++ czy   Paint Pearls Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Subm

HDU 5014 Number Sequence(西安网络赛H题)

HDU 5014 Number Sequence 题目链接 思路:对于0-n,尽量不让二进制中的1互相消掉就是最优的,那么只要两个数只要互补就可以了,这样每次从最大的数字,可以找到和他互补的数字,然后这个区间就能确定了,然后剩下的递归下去为一个子问题去解决 代码: #include <cstdio> #include <cstring> const int N = 100005; int n, a[N], ans[N]; int cnt[N]; int count(int x) {