首页 > 其他 > 详细

后缀数组

时间:2014-03-30 09:45:23      阅读:458      评论:0      收藏:0      [点我收藏+]
bubuko.com,布布扣
后缀数组模板:
bubuko.com,布布扣
bubuko.com,布布扣
bubuko.com,布布扣
 1 //rank从0开始
 2 //sa从1开始,因为最后一个字符(最小的)排在第0位
 3 //high从2开始,因为表示的是sa[i-1]和sa[i]
 4 #define M 220000
 5 int rank[M],sa[M],X[M],Y[M],high[M],init[M];
 6 int buc[M];
 7 void calhigh(int n) {
 8     int i , j , k = 0;
 9     for(i = 1 ; i <= n ; i++) rank[sa[i]] = i;
10     for(i = 0 ; i < n ; high[rank[i++]] = k)
11         for(k?k--:0 , j = sa[rank[i]-1] ; init[i+k] == init[j+k] ; k++);
12 }
13 bool cmp(int *r,int a,int b,int l) {
14     return (r[a] == r[b] && r[a+l] == r[b+l]);
15 }
16 void suffix(int n,int m = 128) {
17     int i , l , p , *x = X , *y = Y;
18     for(i = 0 ; i < m ; i ++) buc[i] = 0;
19     for(i = 0 ; i < n ; i ++) buc[ x[i] = init[i]  ] ++;
20     for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
21     for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
22     for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
23         p = 0;
24         for(i = n-l ; i < n ; i ++) y[p++] = i;
25         for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
26         for(i = 0 ; i < m ; i ++) buc[i] = 0;
27         for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
28         for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
29         for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
30         for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
31             x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
32     }
33     calhigh(n-1);//后缀数组关键是求出high,所以求sa的时候顺便把rank和high求出来
34 }
35  
36  
37 //当需要反复询问两个后缀的最长公共前缀时用到RMQ
38 int Log[M];
39 int best[20][M];
40 void initRMQ(int n) {//初始化RMQ
41     for(int i = 1; i <= n ; i ++) best[0][i] = high[i];
42     for(int i = 1; i <= Log[n] ; i ++) {
43         int limit = n - (1<<i) + 1;
44         for(int j = 1; j <= limit ; j ++) {
45             best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]); 
46         }
47     }
48 }
49 int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
50     a = rank[a];    b = rank[b];
51     if(a > b) swap(a,b);
52     a ++;
53     int t = Log[b - a + 1];
54     return min(best[t][a] , best[t][b - (1<<t) + 1]);
55 }
56  
57  
58 int main() {
59     //预处理每个数字的Log值,常数优化,用于RMQ
60     Log[0] = -1;
61     for(int i = 1; i <= M ; i ++) {
62         Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
63     }
64     //*******************************************
65     //    n为数组长度,下标0开始
66     //    将初始数据,保存在init里,并且保证每个数字都比0大
67     //    m = max{ init[i] } + 1
68     //    一般情况下大多是字符操作,所以128足够了
69     //*******************************************
70     init[n] = 0;
71     suffix(n+1,m);
72  
73     initRMQ(n);
74 }
View Code
bubuko.com,布布扣

 

hdu2890

http://acm.hdu.edu.cn/showproblem.php?pid=2890

整个题意是求最长公共不重叠子串且数量要大于k。

 

在开始的时候要对串离散化。

直接二分答案判断是否可行。直接将heigh数组扫一遍,然后贪心是否满足,在外面开个标记,记录满足的后缀。(开始用map,vector,码码能力还是这么搓。)

 

bubuko.com,布布扣
bubuko.com,布布扣
  1 //rank从0开始
  2 //sa从1开始,因为最后一个字符(最小的)排在第0位
  3 //high从2开始,因为表示的是sa[i-1]和sa[i]
  4 #define M 220000
  5 #include <cstdio>
  6 #include <cstring>
  7 #include <iostream>
  8 #include <map>
  9 #include <vector>
 10 #include <algorithm>
 11 
 12 using namespace std;
 13 
 14 
 15 int rank[M],sa[M],X[M],Y[M],high[M],init[M];
 16 int buc[M];
 17 void calhigh(int n) {
 18     int i , j , k = 0;
 19     for(i = 1 ; i <= n ; i++) rank[sa[i]] = i;
 20     for(i = 0 ; i < n ; high[rank[i++]] = k)
 21         for(k?k--:0 , j = sa[rank[i]-1] ; init[i+k] == init[j+k] ; k++);
 22 }
 23 bool cmp(int *r,int a,int b,int l) {
 24     return (r[a] == r[b] && r[a+l] == r[b+l]);
 25 }
 26 void suffix(int n,int m = 128) {
 27     int i , l , p , *x = X , *y = Y;
 28     for(i = 0 ; i < m ; i ++) buc[i] = 0;
 29     for(i = 0 ; i < n ; i ++) buc[ x[i] = init[i]  ] ++;
 30     for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
 31     for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
 32     for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
 33         p = 0;
 34         for(i = n-l ; i < n ; i ++) y[p++] = i;
 35         for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
 36         for(i = 0 ; i < m ; i ++) buc[i] = 0;
 37         for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
 38         for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
 39         for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
 40         for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
 41             x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
 42     }
 43     calhigh(n-1);//后缀数组关键是求出high,所以求sa的时候顺便把rank和high求出来
 44 }
 45 
 46 
 47 //当需要反复询问两个后缀的最长公共前缀时用到RMQ
 48 int Log[M];
 49 int best[20][M];
 50 void initRMQ(int n) {//初始化RMQ
 51     for(int i = 1; i <= n ; i ++) best[0][i] = high[i];
 52     for(int i = 1; i <= Log[n] ; i ++) {
 53         int limit = n - (1<<i) + 1;
 54         for(int j = 1; j <= limit ; j ++) {
 55             best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]);
 56         }
 57     }
 58 }
 59 int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
 60     a = rank[a];    b = rank[b];
 61     if(a > b) swap(a,b);
 62     a ++;
 63     int t = Log[b - a + 1];
 64     return min(best[t][a] , best[t][b - (1<<t) + 1]);
 65 }
 66 
 67 struct node{
 68     int id;
 69     int r;
 70     friend bool operator < (const node &a,const node &b){
 71         if(a.r<b.r)return 1;
 72         return 0;
 73     }
 74 };
 75 int g[100000];
 76 int r2k[100000];
 77 node mp[100000];
 78 int ans;
 79 int n,k;
 80 bool check(int mid,int sz){
 81 
 82     int i;
 83     int flag=0,res=1;
 84     sort(g,g+sz);
 85     for(i=1;i<sz;i++){
 86         if(g[i]-g[flag]>=mid){
 87             res++;flag=i;
 88         }
 89     }
 90     if(res>=k)return 1;
 91     return 0;
 92 }
 93 bool solve(int mid){
 94     int i;
 95     int res;
 96     int flag;
 97     g[0]=sa[1];
 98     int sz=1;
 99     for(i=2;i<=n;i++){
100         if(high[i]>=mid){
101             g[sz++]=sa[i];
102         }
103         else{
104             if(check(mid,sz)){
105                 ans=sa[i-1];
106                 return 1;
107             }
108             else{
109                 sz=0;
110                 g[sz++]=sa[i];
111             }
112         }
113     }
114     if(check(mid,sz)){
115         ans=sa[i-1];
116         return 1;
117     }
118     return 0;
119 }
120 int main() {
121     //预处理每个数字的Log值,常数优化,用于RMQ
122     Log[0] = -1;
123     for(int i = 1; i <= M ; i ++) {
124         Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
125     }
126     //*******************************************
127     //    n为数组长度,下标0开始
128     //    将初始数据,保存在init里,并且保证每个数字都比0大
129     //    m = max{ init[i] } + 1
130     //    一般情况下大多是字符操作,所以128足够了
131     //*******************************************
132     int T;
133     int i,j;
134     int id;
135     int flag;
136     int l,r,mid;
137 
138     cin>>T;
139 
140     while(T--){
141 
142 
143         cin>>n>>k;
144         for(i=0;i<n;i++){
145             scanf("%d",&mp[i].r);
146             mp[i].id=i;
147         }
148         sort(mp,mp+n);
149         id=1;init[mp[0].id]=1;
150         r2k[1]=mp[0].r;
151         for(int i=1;i<n;i++){
152             if(mp[i].r!=mp[i-1].r){
153                 id++;
154                 init[mp[i].id]=id;
155                 r2k[id]=mp[i].r;
156             }
157             else{
158                 init[mp[i].id]=id;
159             }
160         }
161         init[i]=0;
162         suffix(n+1,id+5);
163         l=0,r=n+1;
164         while(l<=r){
165             mid=(l+r)/2;
166             if(solve(mid))
167                 l=mid+1;
168             else
169                 r=mid-1;
170         }
171         cout<<l-1<<endl;
172         for(i=0;i<l-1;i++)
173             printf("%d\n",r2k[init[ans+i]]);
174         if(T!=0)
175         printf("\n");
176     }
177     /*init[n] = 0;
178     suffix(n+1,m);
179 
180     initRMQ(n);*/
181 }
View Code
bubuko.com,布布扣

 

poj2774

 

题意是求两个串的最长公共子串。

 

将两个串连接中间用个非字符集的字符(模板都是转化到init数组,中间用0连接)。在扫一遍heigh数组,条件是i和i-1所对应的后缀分别在两个串内。(

  延伸一下,多个字符串求最长公共子串,将所有字符串连接,思路相同。不过应该会麻烦点。。。

 

bubuko.com,布布扣
bubuko.com,布布扣
//rank从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//high从2开始,因为表示的是sa[i-1]和sa[i]
#include<iostream>
#include<cstdio>
#include<cstring>

#define M 220000
using namespace std;


int rank[M],sa[M],X[M],Y[M],high[M],init[M];
int buc[M];
void calhigh(int n) {
    int i , j , k = 0;
    for(i = 1 ; i <= n ; i++) rank[sa[i]] = i;
    for(i = 0 ; i < n ; high[rank[i++]] = k)
        for(k?k--:0 , j = sa[rank[i]-1] ; init[i+k] == init[j+k] ; k++);
}
bool cmp(int *r,int a,int b,int l) {
    return (r[a] == r[b] && r[a+l] == r[b+l]);
}
void suffix(int n,int m = 128) {
    int i , l , p , *x = X , *y = Y;
    for(i = 0 ; i < m ; i ++) buc[i] = 0;
    for(i = 0 ; i < n ; i ++) buc[ x[i] = init[i]  ] ++;
    for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
    for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
    for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
        p = 0;
        for(i = n-l ; i < n ; i ++) y[p++] = i;
        for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
        for(i = 0 ; i < m ; i ++) buc[i] = 0;
        for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
        for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
        for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
        for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
            x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
    }
    calhigh(n-1);//后缀数组关键是求出high,所以求sa的时候顺便把rank和high求出来
}


//当需要反复询问两个后缀的最长公共前缀时用到RMQ
int Log[M];
int best[20][M];
void initRMQ(int n) {//初始化RMQ
    for(int i = 1; i <= n ; i ++) best[0][i] = high[i];
    for(int i = 1; i <= Log[n] ; i ++) {
        int limit = n - (1<<i) + 1;
        for(int j = 1; j <= limit ; j ++) {
            best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]);
        }
    }
}
int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
    a = rank[a];    b = rank[b];
    if(a > b) swap(a,b);
    a ++;
    int t = Log[b - a + 1];
    return min(best[t][a] , best[t][b - (1<<t) + 1]);
}

char str1[100001],str2[100001];
int main() {
    //预处理每个数字的Log值,常数优化,用于RMQ
    Log[0] = -1;
    for(int i = 1; i <= M ; i ++) {
        Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
    }
    //*******************************************
    //    n为数组长度,下标0开始
    //    将初始数据,保存在init里,并且保证每个数字都比0大
    //    m = max{ init[i] } + 1
    //    一般情况下大多是字符操作,所以128足够了
    //*******************************************
    int i,j;
    int n;
    int l1,r1,l2,r2;
    while(~scanf("%s%s",str1,str2)){
        for(i=0;str1[i]!=\0;i++)init[i]=str1[i]-a+1;
        l1=0,r1=i-1;
        init[i++]=0;
        l2=i;
        for(j=0;str2[j]!=\0;j++)init[i++]=str2[j]-a+1;
        r2=i-1;
        init[i]=0;
        n=i;
        suffix(n+1,128);
        int ans=0;
        for(i=3;i<=n;i++){
            if(high[i]>ans&&(sa[i]>=l1&&sa[i]<=r1&&sa[i-1]>=l2&&sa[i-1]<=r2||
                             sa[i]>=l2&&sa[i]<=r2&&sa[i-1]>=l1&&sa[i-1]<=r1)){
                                ans=high[i];
                             }
        }
        printf("%d\n",ans);
    }
    /*init[n] = 0;
    suffix(n+1,m);

    initRMQ(n);
    */
}
View Code
bubuko.com,布布扣

后缀数组,布布扣,bubuko.com

后缀数组

原文:http://www.cnblogs.com/youyouyou/p/3633306.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!