当前位置:编程学习 > C/C++ >>

POJ 3693 后缀数组 重复次数最多的连续重复子串 倍增法以及D3法

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++;////  
   

补充:软件开发 , C++ ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,