后缀数组 --- WOj 1564 Problem 1564

来源:互联网 发布:呼叫中心网络拓扑图 编辑:程序博客网 时间:2024/06/11 10:32
 Problem 1564 - A - Circle

Problem's Link:   http://acm.whu.edu.cn/land/problem/detail?problem_id=1564


 

Mean: 

给你一个长度不超过1e6的数字串,求第k大的环状数字串的前面那个位置。

 

analyse:

好吧,我承认这是个水题,比赛的时候sb了,因为原来做过后缀自动机求解字符串的环状最小表示法,所以一直用后缀自动机的知识去套k小表示法,比赛的时候太固执了。

这题就是后缀数组的sa[]数组的运用,sa[i]=k表示的是字符串所有的后缀按字典序排序后,第i个后缀排在第k个。

那么我们只需将数字串复制一遍接在原串后面(环状),对s求一遍后缀数字得到sa数组,然后找到sa<=n的第k个sa,那么sa[i]-1就是答案。

为什么?看这张图:

 

连接后的字符串我们可以得到2*n个字符串,但是我们只需关心前n个字符串就可,因为后面的n个字符串其实根本没作用,他们只相当于前n个字符串的前缀的重复出现而已。

所以我们只需要找到满足sa[i]<=n的第k个就行,sa[i]-1就是答案。

 

 

 

 

Time complexity: O(n)

 

Source code:  

 

//  Memory   Time//  1347K     0MS//   by : crazyacking//   2015-04-20-19.00#include<map>#include<queue>#include<stack>#include<cmath>#include<cstdio>#include<vector>#include<string>#include<cstdlib>#include<cstring>#include<climits>#include<iostream>#include<algorithm>#define MAXN 1000010#define LL long longusing namespace std;const int maxn = 2002000;int Rank[maxn],wb[maxn],wv[maxn],wss[maxn];bool cmp(int *r,int a,int b,int l){    return r[a]==r[b] && r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m){    int i,j,p,*x=Rank,*y=wb,*t;    for(i=0;i<m;i++) wss[i]=0;    for(i=0;i<n;i++) wss[x[i]=r[i]]++;    for(i=1;i<m;i++) wss[i]+=wss[i-1];    for(i=n-1;i>=0;i--)       sa[--wss[x[i]]]=i;    for(j=1,p=1;p<n;j*=2,m=p)    {        for(p=0,i=n-j;i<n;i++) y[p++]=i;        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;        for(i=0;i<n;i++) wv[i]=x[y[i]];        for(i=0;i<m;i++) wss[i]=0;        for(i=0;i<n;i++) wss[wv[i]]++;        for(i=1;i<m;i++) wss[i]+=wss[i-1];        for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;    }    return;}char s[maxn];int r[maxn],sa[maxn];int main(){    int n,k;    while(~scanf("%d %d",&n,&k))    {            getchar();            gets(s);            int len = strlen(s),i;            for(int i=len;i<len*2;++i) s[i]=s[i-len];            s[len*2]='\0';            len=len*2;        for(int i=0;i<len;++i)                s[i]+='a'-'0';        len++;        for(i=0; i<len-1; i++)            r[i] = s[i]-'a'+1;        r[len-1] = 0;        da(r,sa,len,30);        int idx=0;        for(int i=1;i<len;++i)        {                if(sa[i]+1<=n)                {                        idx++;                        if(idx==k) printf("%d\n",sa[i]!=0?sa[i]:n);                }        }    }    return 0;}
View Code

 

 

 

#include<cstdio>#include<vector>#include<cstring>#define MAXN 1000010using namespace std;int n,k,len,si,now;char s[MAXN];vector<int> num;int GetAns(vector<int>tp,int tk,int l){        if(tp.size()==1 || l==n-1) return tp[0];        vector<int> cnt[10];        si=tp.size();        for(int i=0;i<si;++i)                cnt[s[ (tp[i]+l)%n ]-'0'].push_back(tp[i]);        now=0;        while(tk>cnt[now].size())        {                tk-=cnt[now].size();now++;        }        return GetAns(cnt[now],tk,l+1);}int main(){        while(~scanf("%d %d",&n,&k))        {                scanf("%s",s);                len=strlen(s);                num.clear();                for(int i=1;i<=len;++i) num.push_back(i);                printf("%d\n",GetAns(num,k,0));        }        return 0;}
View Code

 

0 0