poj 3450/poj 3080 多串最长公共子串 后缀数组

题意:就是求多串的最长公共子串

思路:子串可以看作是字符串中后缀的前缀。把这些字符串连接起来,中间用不能出现的状态隔开,然后求其height数组,二分枚举一个字符串的子串长度,看是否存在连续n个最长公共前缀的长度都大于mid,并且这些公共长度分别属于n个字符串。

poj 3080 

保证最长的前提下,保证字典序,字典序是用sa数组来保证的,最先找到的公共前缀一定是字典序较小的,然后这些公共前缀的开头都是一样的,所以输出的公共子串的开头也是以sa[i]为头的。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 800100;
char str[maxn];
int num[maxn] , loc[maxn];//匹配数组和标记数组 
int sa[maxn] , rank[maxn] , height[maxn];
int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn];
int T;
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) {
    int i , j , p;
    int *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) {
    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 ++);    
    }    
}
int vis[4010];
char ss[maxn];
bool check(int mid , int len) {
    int i , j , cnt;
    cnt = 0;
    memset(vis,0,sizeof(vis));
    for(i = 2 ; i < len ; i ++) {
        //printf("运行 %d %d\n",height[i],sa[i]);
        if(height[i] < mid) {
            memset(vis,0,sizeof(vis));
            cnt = 0;
            continue;    
        }    
        if(!vis[loc[sa[i-1]]]) {
            vis[loc[sa[i-1]]] = 1;
            cnt++;    
        }
        if(!vis[loc[sa[i]]]) {
            vis[loc[sa[i]]] = 1;
            cnt ++;    
        }
        if(cnt==T) {
            for(j = 0 ; j < mid ; j ++)
            ss[j] = num[sa[i]+j] + 'A' - 2;  
            return 1;  
        } ss[mid] = '\0';
    }
    return 0;        
}
int main() {
    int i , len , g , j , N;
    scanf("%d",&N);
    while(N--)
    {
        scanf("%d",&T);
        //if(T==0) break;
        g = 0;
        int temp = 30;
        for(i = 0 ; i < T ; i ++) {
            scanf("%s",str);
            len = strlen(str);
            for(j = 0 ; j < len ; j ++) {
                loc[g] = i;//标记字符串块儿
                num[g++] = str[j] - 'A' + 2;    
            }
            loc[g] = temp;
            num[g++] = temp++;   //一些不会出现的状态 
        }
        //loc[g]=-1;
        num[g] = 0;
        da(num , g+1 , temp);
        calheight(num , g);  
        int left = 0 , right = strlen(str) , mid;
        int ans = 0;
        while(left <= right) {
            mid = (left+right)/2;
            if(check(mid , g)) {
                left = mid + 1;    
            } else {
                right = mid - 1;  
            }    
        }
        if(strlen(ss)>=3)
        printf("%s\n",ss);
        else printf("no significant commonalities\n"); 
    }       
}


poj 3450 基本是一个题

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 800100;
char str[maxn];
int num[maxn] , loc[maxn];//匹配数组和标记数组 
int sa[maxn] , rank[maxn] , height[maxn];
int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn];
int T;
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) {
    int i , j , p;
    int *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) {
    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 ++);    
    }    
}
int vis[4010];
char ss[maxn];
bool check(int mid , int len) {
    int i , j , cnt;
    cnt = 0;
    memset(vis,0,sizeof(vis));
    for(i = 2 ; i < len ; i ++) {
        //printf("运行 %d %d\n",height[i],sa[i]);
        if(height[i] < mid) {
            memset(vis,0,sizeof(vis));
            cnt = 0;
            continue;    
        }    
        if(!vis[loc[sa[i-1]]]) {
            vis[loc[sa[i-1]]] = 1;
            cnt++;    
        }
        if(!vis[loc[sa[i]]]) {
            vis[loc[sa[i]]] = 1;
            cnt ++;    
        }
        if(cnt==T) {
            for(j = 0 ; j < mid ; j ++)
            ss[j] = num[sa[i]+j] + 'a' - 2;  
            return 1;  
        } ss[mid] = '\0';
    }
    return 0;        
}
int main() {
    int i , len , g , j;
    while(scanf("%d",&T)!=EOF) {
        if(T==0) break;
        g = 0;
        int temp = 30;
        for(i = 0 ; i < T ; i ++) {
            scanf("%s",str);
            len = strlen(str);
            for(j = 0 ; j < len ; j ++) {
                loc[g] = i;//标记字符串块儿
                num[g++] = str[j] - 'a' + 2;    
            }
            loc[g] = temp;
            num[g++] = temp++;   //一些不会出现的状态 
        }
        //loc[g]=-1;
        num[g] = 0;
        da(num , g+1 , temp);
        calheight(num , g);  
        int left = 0 , right = strlen(str) , mid;
        int ans = 0;
        while(left <= right) {
            mid = (left+right)/2;
            if(check(mid , g)) {
                left = mid + 1;
                ans = mid;    
            } else {
                right = mid - 1;  
            }    
        }
        if(ans!=0)
        printf("%s\n",ss);
        else printf("IDENTITY LOST\n"); 
    }       
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值