您的位置:首页 > 其它

后缀数组 --- WOj 1564 Problem 1564 - A - Circle

2017-03-05 17:15 393 查看
 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 long
using 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 && 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
 

 

[b] 






#include<cstdio>
#include<vector>
#include<cstring>
#define MAXN 1000010
using 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
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: