poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串

题目链接

题意:输入N(2 <= N <= 4000)个长度不超过200的字符串,输出字典序最小的最长公共连续子串;

思路:将所有的字符串中间加上分隔符,注:分隔符只需要和输入的字符不同,且各自不同即可,没有必要是最小的字符;

连接后缀数组求解出height之后二分长度,由于height是根据sa数组建立的,所以前面符合的就是字典序最小的,直接找到就停止即可;

ps: 把之前的模板简化了下,A题才是关键;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<time.h>
using namespace std;
#define MS0(a) memset(a,0,sizeof(a))
typedef long long ll;
const int MAXN = 1000005;
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],wv[MAXN];
int cmp(int *r, int a, int b, int l){
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void build_sa(int *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
    int i, j, p, *x = t, *y = t2;
    for(i = 0; i < m; i ++) c[i] = 0;
    for(i = 0; i < n; i ++) c[x[i] = r[i]] ++;
    for(i = 1; i < m; i ++) c[i] += c[i-1];
    for(i = n-1; i >= 0; i --) sa[--c[x[i]]] = i;
    for(j = 1, p = 1; p < n; j <<= 1, 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 ++) c[i] = 0;
        for(i = 0; i < n; i ++) c[wv[i]] ++;
        for(i = 1; i < m; i ++) c[i] += c[i-1];
        for(i = n-1; i >= 0; i --) sa[--c[wv[i]]] = y[i];
        for(swap(x,y), 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++;
        }
    }
}
int rk[MAXN],height[MAXN];
void getHeight(int *r,int n)
{
    for(int i = 1;i <= n;i++) rk[sa[i]] = i; // rk[i]:后缀i在sa[]中的下标
    for(int i = 0,j,k = 0; i < n; height[rk[i++]] = k){
        for(k? k--:0 ,j = sa[rk[i] - 1];r[i+k] == r[j+k];k++);
    }
}
int d[MAXN],num[MAXN],vs[4444],T,len,tot;
char tt[222];
bool check(int L)
{
    MS0(vs);
    int cnt = 0;
    for(int i = 2;i <= tot;i++){
        if(height[i] < L){
            MS0(vs);
            cnt = 0;
            continue;
        }
        if(!vs[d[sa[i]]]){
            vs[d[sa[i]]] = 1;cnt++;
        }
        if(!vs[d[sa[i-1]]]){
            vs[d[sa[i-1]]] = 1;cnt++;
        }
        if(cnt == T){ //对于同一个长度,我们只取第一次出现的符合条件的字符串;
            for(int j = 0; j<L; j++)
                    tt[j] = num[sa[i]+j];
                tt[L] = '';
            return true;
        }
    }
    return false;
}
char str[222];
int main()
{
    while(scanf("%d",&T) == 1 && T){
        tot = 0;
        for(int i = 1;i <= T;i++){
            scanf("%s",str);
            len = strlen(str);
            for(int j = 0;j < len;j++)
                num[tot] = str[j],d[tot++] = i;
            num[tot] = 'z'+i,d[tot++] = 'z'+i;// '#' + i竟然WA了
        }
        num[tot] = 0;//最后添加一个最小字符;
        build_sa(num,tot+1,5155);
        getHeight(num,tot);
        int ans = 0,l = 1,r = len,mid,id;
        while(l <= r){
            mid = l + r >> 1;
            if(check(mid)){ans = mid,l = mid + 1;}
            else r = mid - 1;
        }
        if(ans){
            printf("%s
",tt);
        }
        else puts("IDENTITY LOST");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/hxer/p/5380271.html