BZOJ-3172: [Tjoi2013]单词(SA+RMQ+二分查找)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3172

把所有单词中间加入一个其他字符,然后建成后缀数组,明显,以相同单词为前缀的后缀在SA里面是一个区间,那么二分查找枚举区间端点即可。

代码(第二次写,很长很丑)(刚开始快排在BZOJ上过了,在WIKI上却TLEL了,搞的只能硬着头皮去写基排):

#include  
#include  
#include  
#include  
   
using namespace std; 
   
#define MAXN 210 
#define MAXL 1501000 
#define MAXB 23 
   
char word[MAXL],s[MAXL]; 
int fir[MAXN],len[MAXN],n,N=0; 
int st[MAXL][MAXB],B=0; 
    
int rank[MAXL],sa[MAXL],he[MAXL],m=0;
 
int head[MAXL],tail[MAXL],next[MAXL],M,x[MAXL],y[MAXL],w[MAXL],r[MAXL];
   
void Sa() {
    int M=N,Nn,b=1;
        for (int i=0;i++=0?j-1:0; 
            he[rank[i]]=j; 
            for (int k=j;i+k<=N&&sa[rank[i]-1]+k<=N&&word[i+k]==word[sa[rank[i]-1]+k];k++) he[rank[i]]++; 
            j=he[rank[i]]; 
        } 
    } 
} 
   
void Init_rmq() { 
    B=int(log2(N))+1; 
    for (int i=0;i++1) {
                    int mid=(l+r)>>1;
                    if (Min(mid,X)>=len[i]) r=mid
                        ;  else l=mid;
                }
                L=l;
            }
        if (he[X+1]=len[i]) {
            R=N;
        } else {
            int l=X+1,r=N;
            while (r-l>1) {
                int mid=(l+r)>>1;
                if (Min(X+1,mid)>=len[i]) l=mid
                    ; else r=mid;
            }
            R=l;
        }
        putint(R-L+1);
    } 
    return 0; 
}

你可能感兴趣的:(BZOJ-3172: [Tjoi2013]单词(SA+RMQ+二分查找))