BZOJ 3172([Tjoi2013]单词-后缀数组第一题+RMQ)

 

3172: [Tjoi2013]单词

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 268   Solved: 145
[ Submit][ Status]

Description

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文 
中出现多少次。

Input

第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6

Output

输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。

Sample Input

3
a
aa
aaa

Sample Output

6
3
1

 

 

上一次写RMQ是什么时候?(喂,离题了)

好吧……

第一题后缀数组

不想写下去了……(快哭了TNT)

这题在BZOJ上内存很容易开过(5人组-》TLE/CE/MLE/RE/AC)

大家要是这题RE把数组开小点。别忘了[RMQ*20]数组+数组之和 //省空间

 

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<iostream>

#include<algorithm>

#include<functional>

#include<cmath>

#include<cctype>

using namespace std;

#define For(i,n) for(int i=1;i<=n;i++)

#define Rep(i,n) for(int i=0;i<n;i++)

#define Fork(i,k,n) for(int i=k;i<=n;i++)

#define ForD(i,n) for(int i=n;i;i--)

#define Forp(x) for(int p=pre[x];p;p=next[p])

#define RepD(i,n) for(int i=n;i>=0;i--)

#define MEM(a) memset(a,0,sizeof(a))

#define MEMI(a) memset(a,127,sizeof(a))

#define MEMi(a) memset(a,128,sizeof(a))

#define INF (2139062143)

#define F (1000000009)

#define MAXN (300+10)

#define MAXL (1000200+10)

#define eps (1e-9)

typedef long long ll;

char s[MAXL];

int n,pre[MAXN],tai[MAXN];

int w[MAXL],sa[MAXL],wa[MAXL*2]={0},wb[MAXL*2]={0};

// x-->上一行 y->下一行sa右值   wv-->y的左值  sa-->上次排名(求) 

bool cmp(int *a,int x,int y,int l){return (a[x]==a[y]&&a[x+l]==a[y+l]);}

void suffix_array(int n,int m)

{

   int *x=wa,*y=wb;

   For(i,m) w[i]=0;

   For(i,n) w[x[i]=s[i]]++;

   Fork(i,2,m) w[i]+=w[i-1];

   ForD(i,n) sa[w[x[i]]--]=i;

   for(int j=1,p=0;p<n;j*=2,m=p)

   {

           

      p=0;

      Fork(i,n-j+1,n) y[++p]=i;

      For(i,n) if (j<sa[i]) y[++p]=sa[i]-j;

      

      For(i,m) w[i]=0;

      For(i,n) w[x[i]]++;

      For(i,m) w[i]+=w[i-1];

      

      

      ForD(i,n) sa[w[x[ y[i] ]]--]=y[i];

      //y is release

      

      p=y[sa[1]]=1;

      Fork(i,2,n)

         y[sa[i]]=(p+=(!cmp(x,sa[i-1],sa[i],j)));



      int *t=x;x=y;y=t;

   }

}

int height[MAXL],rank[MAXL]; //height[i] 表示 sa[i]与sa[i-1]的最长公共前缀 

void make_height(char *s,int n)

{

   For(i,n) rank[sa[i]]=i;

   For(i,n)

   {

      if (rank[i]==1) continue; //求height[rank[i]] 

      int j=max(0,height[rank[i-1]]-1),k=sa[rank[i]-1];

      while (s[i+j]==s[k+j]) j++;

      height[rank[i]]=j;

   }

}

int bin[MAXN]={0},f[MAXL][24]={0};

int lcp(int l,int r)

{

   int len=r-l+1,j=(int)log2(len);

   return min(f[l][j],f[r-bin[j]+1][j]);

}

int main()

{

// freopen("bzoj3172.in","r",stdin);

   scanf("%d",&n);pre[1]=1;

   For(i,n)

   {

      scanf("%s",s+pre[i]);

      tai[i]=strlen(s+pre[i]);

      s[pre[i]+tai[i]]='#';

      pre[i+1]=pre[i]+tai[i]+1;

   }

   s[pre[n+1]]=0;

   

   suffix_array(pre[n+1]-1,200);

   

   make_height(s,pre[n+1]-1);

   

   

   int logn=int(log2((double)pre[n+1]-1)+1);

   bin[0]=1;

   For(i,logn) bin[i]=bin[i-1]<<1;

   For(i,pre[n+1]-1) f[i][0]=height[i];

   

   For(j,logn)

      For(i,pre[n+1]-1-(1<<j)+1)

         f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);

   

   For(i,n)

   {

      int tot=1;

      int j=rank[pre[i]]+1;

      {

         int l=2,r=j-1,ans=0;

         while (l<=r)

         {

            int m=l+r>>1;

            if (lcp(m,j-1)>=tai[i]) ans=m,r=m-1;

            else l=m+1;             

         }

         if (ans) ans=j-1-ans+1;

         tot+=ans;

      }

      {

         int l=j,r=pre[n+1]-1,ans=0;

         while (l<=r)

         {

            int m=l+r>>1;

            if (lcp(j,m)>=tai[i]) ans=m,l=m+1;

            else r=m-1;

         }

         if (ans) ans=ans-j+1;

         tot+=ans;

      }

      cout<<tot<<endl;

   }

// while (1);

   return 0;

}


 


 

你可能感兴趣的:(后缀数组)