程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> POJ 3693 後綴數組 重復次數最多的連續重復子串 倍增法以及D3法

POJ 3693 後綴數組 重復次數最多的連續重復子串 倍增法以及D3法

編輯:C++入門知識

n以第k個字符開始的後綴稱為後綴k。
 
【後綴數組SA】後綴數組保存的是一個字符串的所有後綴的排序結果。其中SA[i]保存的是字符串所有的後綴中第i小的後綴的開頭位置。 sa記錄的是排序過後的
【名次數組Rank】名次數組Rank[i]保存的是後綴i在所有後綴中從小到大排列的“名次”。
 
提到後綴數組,離不開三個數組,sa,rank,height,sa[i]存放的是排名第i的是那個後綴,用所在的下表表示。rank[i],表示下表為i的後綴它的排名為多少,和sa互為逆運算,

簡單的說,後綴數組是“排第幾的是誰?”,名次數組是“你排第幾?”。

 對於不懂後綴數組的強烈建議看下 羅穗骞《後綴數組——處理字符串的有力工具》國家ACM論文


 

 

另外下面的題目題解大家自己看下面的博客吧    表示自己看了好久沒看懂怎麼做的  最後找到了 幾個比較好的文章  終於算是搞懂了

關於poj   3693


 


 

[cpp]
#include<stdio.h>  
#include<string.h>  
#include<iostream>  
#include<cstdio>  
#include<vector>  
#include<cstring>  
using namespace std; 
 
const int nMax =1000012; 
 
int  num[nMax]; 
int sa[nMax], rank[nMax], height[nMax]; 
int wa[nMax], wb[nMax], wv[nMax], wd[nMax]; 
int mmin(int a,int b) 

    if(a>b) return b; 
    return a; 

int 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 n, int m){          //  倍增算法 r為待匹配數組  n為總長度 m為字符范圍  
    int i, j, p, *x = wa, *y = wb, *t; 
    for(i = 0; i < m; i ++) wd[i] = 0; 
    for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++; 
    for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 
    for(i = n-1; i >= 0; i --) sa[-- wd[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 ++) wd[i] = 0; 
        for(i = 0; i < n; i ++) wd[wv[i]] ++; 
        for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 
        for(i = n-1; i >= 0; i --) sa[-- wd[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 ++; 
        } 
    } 

 
void calHeight(int *r, int n){           //  求height數組。  
    int i, j, k = 0; 
    for(i = 1; i <= n; i ++) rank[sa[i]] = i; // 1->n  
    for(i = 0; i < n; i++){ 
        for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++); 
        height[rank[i]] = k; 
    } 

 
int Log[nMax]; 
int best[20][nMax];//best[i][j] 表示從j開始的長度為2的i次方的一段元素的最小值  
void initRMQ(int n) 
{//初始化RMQ  
    int  i,j; 
    for(i = 1; i <= n ; i ++) best[0][i] = height[i]; 
    for(i = 1; i <= Log[n] ; i ++)  
    { 
        int limit = n - (1<<i) + 1; 
        for(j = 1; j <= limit ; j ++) 
        { 
            best[i][j] = mmin(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 mmin(best[t][a] , best[t][b - (1<<t) + 1]); 

 
void get_log() 

    int i; 
     Log[0] = -1; 
    for(i=1;i<=nMax;i++) 
    { // 求log2,這麼強大的位運算。。  
        Log[i]=(i&(i-1))?Log[i-1]:Log[i-1] + 1 ; 
    } 

char str[nMax]; 
int ans[nMax]; 
 
int main() 

    int i,j,n,cas=0;; 
    get_log(); 
    while(scanf("%s",str)!=EOF) 
    { 
        n=strlen(str); 
        if(n==1&&str[0]=='#')  break; 
        for(i=0;i<n;i++) 
        { 
            num[i]=str[i]-'a'+1; 
        } 
        num[n]=0;/////////  
        da(num,n+1,30); 
        calHeight(num,n); 
        initRMQ(n); 
        int l,mmax=-1,cnt,pos,t=0; 
        for(l=1;l<n;l++) 
        { 
            for(i=0;i+l<n;i+=l) 
            { 
                int k=lcp(i,i+l); 
                cnt=k/l+1; 
                pos=k%l; 
                pos=l-pos; 
                pos=i-pos; 
                if(pos>=0&&k%l!=0&&lcp(pos,pos+l)>=k) cnt++;////  
                if(cnt>mmax) 
                { 
                    mmax=cnt;  
                    t=0; 
                    ans[t++]=l; 
                }  
                else if(cnt==mmax)   
                { 
                    ans[t++]=l;                  
                } 
            } 
             
        } 
        int flag=0,start; 
        for(i=1;i<=n;i++) 
        { 
            for(j=0;j<t;j++) 
            { 
                l=ans[j]; 
                if(lcp(sa[i],sa[i]+l)>=(mmax-1)*l) 
                { 
                    start=sa[i]; 
                    l=l*mmax; 
                    flag=1; 
                    break; 
                     
                } 
            } 
            if(flag) break; 
        } 
        printf("Case %d: ",++cas);   
        for (int i=0;i<l;i++) printf("%c",str[start+ i]);   
        printf("\n");   
         
         
    } 
    return 0; 

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

const int nMax =1000012;

int  num[nMax];
int sa[nMax], rank[nMax], height[nMax];
int wa[nMax], wb[nMax], wv[nMax], wd[nMax];
int mmin(int a,int b)
{
 if(a>b) return b;
 return a;
}
int 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 n, int m){          //  倍增算法 r為待匹配數組  n為總長度 m為字符范圍
    int i, j, p, *x = wa, *y = wb, *t;
    for(i = 0; i < m; i ++) wd[i] = 0;
    for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
    for(i = 1; i < m; i ++) wd[i] += wd[i-1];
    for(i = n-1; i >= 0; i --) sa[-- wd[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 ++) wd[i] = 0;
        for(i = 0; i < n; i ++) wd[wv[i]] ++;
        for(i = 1; i < m; i ++) wd[i] += wd[i-1];
        for(i = n-1; i >= 0; i --) sa[-- wd[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 ++;
        }
    }
}

void calHeight(int *r, int n){           //  求height數組。
    int i, j, k = 0;
    for(i = 1; i <= n; i ++) rank[sa[i]] = i; // 1->n
    for(i = 0; i < n; i++){
        for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
        height[rank[i]] = k;
    }
}

int Log[nMax];
int best[20][nMax];//best[i][j] 表示從j開始的長度為2的i次方的一段元素的最小值
void initRMQ(int n)
{//初始化RMQ
 int  i,j;
 for(i = 1; i <= n ; i ++) best[0][i] = height[i];
 for(i = 1; i <= Log[n] ; i ++)
 {
  int limit = n - (1<<i) + 1;
  for(j = 1; j <= limit ; j ++)
  {
   best[i][j] = mmin(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 mmin(best[t][a] , best[t][b - (1<<t) + 1]);
}

void get_log()
{
 int i;
     Log[0] = -1;
 for(i=1;i<=nMax;i++)
 { // 求log2,這麼強大的位運算。。
  Log[i]=(i&(i-1))?Log[i-1]:Log[i-1] + 1 ;
 }
}
char str[nMax];
int ans[nMax];

int main()
{
 int i,j,n,cas=0;;
    get_log();
 while(scanf("%s",str)!=EOF)
 {
  n=strlen(str);
  if(n==1&&str[0]=='#')  break;
  for(i=0;i<n;i++)
  {
   num[i]=str[i]-'a'+1;
  }
  num[n]=0;/////////
  da(num,n+1,30);
  calHeight(num,n);
  initRMQ(n);
  int l,mmax=-1,cnt,pos,t=0;
  for(l=1;l<n;l++)
  {
   for(i=0;i+l<n;i+=l)
   {
    int k=lcp(i,i+l);
    cnt=k/l+1;
    pos=k%l;
    pos=l-pos;
    pos=i-pos;
    if(pos>=0&&k%l!=0&&lcp(pos,pos+l)>=k) cnt++;////
    if(cnt>mmax)
    {
     mmax=cnt;
     t=0;
     ans[t++]=l;
    }
    else if(cnt==mmax) 
    {
     ans[t++]=l;                
    }
   }
   
  }
  int flag=0,start;
  for(i=1;i<=n;i++)
  {
   for(j=0;j<t;j++)
   {
    l=ans[j];
    if(lcp(sa[i],sa[i]+l)>=(mmax-1)*l)
    {
     start=sa[i];
     l=l*mmax;
     flag=1;
     break;
     
    }
   }
   if(flag) break;
  }
  printf("Case %d: ",++cas); 
  for (int i=0;i<l;i++) printf("%c",str[start+ i]); 
  printf("\n"); 
  
  
 }
 return 0;
} 注意 千萬注意  數組是從0開始的   所以 rank  sa 也是從0開始的

  D3 做法


[cpp]
#include <stdio.h>  
#include<string.h>  
#define maxn 1000001  
 
#define F(x) ((x)/3+((x)%3==1?0:tb))  
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)  
int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; 
int c0(int *r,int a,int b) 
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];} 
int c12(int k,int *r,int a,int b) 
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1); 
 else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];} 
void sort(int *r,int *a,int *b,int n,int m) 

     int i; 
     for(i=0;i<n;i++) wv[i]=r[a[i]]; 
     for(i=0;i<m;i++) ws[i]=0; 
     for(i=0;i<n;i++) ws[wv[i]]++; 
     for(i=1;i<m;i++) ws[i]+=ws[i-1]; 
     for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i]; 
     return; 

void dc3(int *r,int *sa,int n,int m)      // r為待匹配數組  n為總長度 m為字符范圍  

     int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p; 
     r[n]=r[n+1]=0; 
     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i; 
     sort(r+2,wa,wb,tbc,m); 
     sort(r+1,wb,wa,tbc,m); 
     sort(r,wa,wb,tbc,m); 
     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++) 
     rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++; 
     if(p<tbc) dc3(rn,san,tbc,p); 
     else for(i=0;i<tbc;i++) san[rn[i]]=i; 
     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3; 
     if(n%3==1) wb[ta++]=n-1; 
     sort(r,wb,wa,ta,m); 
     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i; 
     for(i=0,j=0,p=0;i<ta && j<tbc;p++) 
     sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++]; 
     for(;i<ta;p++) sa[p]=wa[i++]; 
     for(;j<tbc;p++) sa[p]=wb[j++]; 
     return; 

int rank[maxn],height[maxn]; 
void calheight(int *r,int *sa,int n) //  求height數組。  

     int i,j,k=0; 
     for(i=1;i<=n;i++) rank[sa[i]]=i; 
     for(i=0;i<n;height[rank[i++]]=k) 
     for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 
     return; 

int RMQ[maxn]; 
int mm[maxn]; 
int best[20][maxn];//best[i][j] 表示從j開始的長度為2的i次方的一段元素的最小值  
void initRMQ(int n) 

     int i,j,a,b; 
     for(mm[0]=-1,i=1;i<=n;i++) 
     mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; 
     for(i=1;i<=n;i++) best[0][i]=i; 
     for(i=1;i<=mm[n];i++) 
     for(j=1;j<=n+1-(1<<i);j++) 
     { 
       a=best[i-1][j]; 
       b=best[i-1][j+(1<<(i-1))]; 
       if(RMQ[a]<RMQ[b]) best[i][j]=a; 
       else best[i][j]=b; 
     } 
     return; 

int askRMQ(int a,int b)//詢問a,b後綴的最長公共前綴  

    int t; 
    t=mm[b-a+1];b-=(1<<t)-1; 
    a=best[t][a];b=best[t][b]; 
    return RMQ[a]<RMQ[b]?a:b; 

int lcp(int a,int b) 

    int t; 
    a=rank[a];b=rank[b]; 
    if(a>b) {t=a;a=b;b=t;} 
    return(height[askRMQ(a+1,b)]); 

 
char c; 
int r[maxn*3],sa[maxn*3]; 
int ans[maxn]; 
char str[maxn*3]; 
int main() 

    int i,j,n,cas=0; 
    while(scanf("%s",str)!=EOF) 
    { 
        n=strlen(str); 
         if(n==1&&str[0]=='#') break; 
          for(i=0;i<n;i++)  r[i]=str[i]-'a'+1;  
      r[n]=0; 
      dc3(r,sa,n+1,30);//千萬注意是n+1  
      calheight(r,sa,n); 
      for(i=1;i<=n;i++) RMQ[i]=height[i]; 
      initRMQ(n); 
       
      /* 
        for(i=0; i<n+1; i++)  // rank[i] : suffix(i)排第幾
           printf("rank[%d] =  %d\n",i,rank[i]);
        printf("\n");
        for(i=0; i<n+1; i++)   // sa[i] : 排在第i個的是誰
           printf("sa[%d] =  %d\n",i,sa[i]);
       */ 
        int l,mmax=-1,cnt,pos,t=0; 
        for(l=1;l<n;l++) 
        { 
            for(i=0;i+l<n;i+=l) 
            { 
                int k=lcp(i,i+l); 
                cnt=k/l+1; 
                pos=k%l; 
                pos=l-pos; 
                pos=i-pos; 
                if(pos>=0&&k%l!=0&&lcp(pos,pos+l)>=k) cnt++;////  
                if(cnt>mmax) 
                { 
                    mmax=cnt;  
                    t=0; 
                    ans[t++]=l; 
                }  
                else if(cnt==mmax)   
                { 
                    ans[t++]=l;                  
                } 
            } 
             
        } 
        int flag=0,start; 
        for(i=1;i<=n;i++) 
        { 
            for(j=0;j<t;j++) 
            { 
                l=ans[j]; 
                if(lcp(sa[i],sa[i]+l)>=(mmax-1)*l) 
                { 
                    start=sa[i]; 
                    l=l*mmax; 
                    flag=1; 
                    break; 
                     
                } 
            } 
            if(flag) break; 
        } 
        printf("Case %d: ",++cas);   
        for (int i=0;i<l;i++) printf("%c",str[start+ i]);   
        printf("\n");   
         
         
    } 
    return 0; 

#include <stdio.h>
#include<string.h>
#define maxn 1000001

#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int c0(int *r,int a,int b)
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
int c12(int k,int *r,int a,int b)
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
 else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
void sort(int *r,int *a,int *b,int n,int m)
{
     int i;
     for(i=0;i<n;i++) wv[i]=r[a[i]];
     for(i=0;i<m;i++) ws[i]=0;
     for(i=0;i<n;i++) ws[wv[i]]++;
     for(i=1;i<m;i++) ws[i]+=ws[i-1];
     for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i];
     return;
}
void dc3(int *r,int *sa,int n,int m)      // r為待匹配數組  n為總長度 m為字符范圍
{
     int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
     r[n]=r[n+1]=0;
     for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
     sort(r+2,wa,wb,tbc,m);
     sort(r+1,wb,wa,tbc,m);
     sort(r,wa,wb,tbc,m);
     for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
     rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
     if(p<tbc) dc3(rn,san,tbc,p);
     else for(i=0;i<tbc;i++) san[rn[i]]=i;
     for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
     if(n%3==1) wb[ta++]=n-1;
     sort(r,wb,wa,ta,m);
     for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
     for(i=0,j=0,p=0;i<ta && j<tbc;p++)
     sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
     for(;i<ta;p++) sa[p]=wa[i++];
     for(;j<tbc;p++) sa[p]=wb[j++];
     return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n) //  求height數組。
{
     int i,j,k=0;
     for(i=1;i<=n;i++) rank[sa[i]]=i;
     for(i=0;i<n;height[rank[i++]]=k)
     for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
     return;
}
int RMQ[maxn];
int mm[maxn];
int best[20][maxn];//best[i][j] 表示從j開始的長度為2的i次方的一段元素的最小值
void initRMQ(int n)
{
     int i,j,a,b;
     for(mm[0]=-1,i=1;i<=n;i++)
     mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
     for(i=1;i<=n;i++) best[0][i]=i;
     for(i=1;i<=mm[n];i++)
     for(j=1;j<=n+1-(1<<i);j++)
     {
       a=best[i-1][j];
       b=best[i-1][j+(1<<(i-1))];
       if(RMQ[a]<RMQ[b]) best[i][j]=a;
       else best[i][j]=b;
     }
     return;
}
int askRMQ(int a,int b)//詢問a,b後綴的最長公共前綴
{
    int t;
    t=mm[b-a+1];b-=(1<<t)-1;
    a=best[t][a];b=best[t][b];
    return RMQ[a]<RMQ[b]?a:b;
}
int lcp(int a,int b)
{
    int t;
    a=rank[a];b=rank[b];
    if(a>b) {t=a;a=b;b=t;}
    return(height[askRMQ(a+1,b)]);
}

char c;
int r[maxn*3],sa[maxn*3];
int ans[maxn];
char str[maxn*3];
int main()
{
    int i,j,n,cas=0;
    while(scanf("%s",str)!=EOF)
    {
        n=strlen(str);
   if(n==1&&str[0]=='#') break;
          for(i=0;i<n;i++)  r[i]=str[i]-'a'+1;
      r[n]=0;
      dc3(r,sa,n+1,30);//千萬注意是n+1
      calheight(r,sa,n);
      for(i=1;i<=n;i++) RMQ[i]=height[i];
      initRMQ(n);
     
   /*
        for(i=0; i<n+1; i++)  // rank[i] : suffix(i)排第幾
           printf("rank[%d] =  %d\n",i,rank[i]);
        printf("\n");
        for(i=0; i<n+1; i++)   // sa[i] : 排在第i個的是誰
           printf("sa[%d] =  %d\n",i,sa[i]);
       */
  int l,mmax=-1,cnt,pos,t=0;
  for(l=1;l<n;l++)
  {
   for(i=0;i+l<n;i+=l)
   {
    int k=lcp(i,i+l);
    cnt=k/l+1;
    pos=k%l;
    pos=l-pos;
    pos=i-pos;
    if(pos>=0&&k%l!=0&&lcp(pos,pos+l)>=k) cnt++;////
    if(cnt>mmax)
    {
     mmax=cnt;
     t=0;
     ans[t++]=l;
    }
    else if(cnt==mmax) 
    {
     ans[t++]=l;                
    }
   }
   
  }
  int flag=0,start;
  for(i=1;i<=n;i++)
  {
   for(j=0;j<t;j++)
   {
    l=ans[j];
    if(lcp(sa[i],sa[i]+l)>=(mmax-1)*l)
    {
     start=sa[i];
     l=l*mmax;
     flag=1;
     break;
     
    }
   }
   if(flag) break;
  }
  printf("Case %d: ",++cas); 
  for (int i=0;i<l;i++) printf("%c",str[start+ i]); 
  printf("\n"); 
  
  
    }
    return 0;
}

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved