zoukankan      html  css  js  c++  java
  • poj

    题意:输入2个长度不超过100000的字符串,问它们最长公共子串的长度。

    题目链接:http://poj.org/problem?id=2774

    ——>>后缀数组!后缀数组!微笑~从LJ的《训练指南》,到许智磊的论文+PPT,吉大的模版,学长的博客,这路还真不容易走。。。

    最后决定用LJ《训练指南》的写法,感觉挺精辟的。

    合并两个串,中间放一个特殊字符,根据条件(len为第一个串的长度,n为合并后串的长度):

    (sa[i] >= 0 && sa[i] < len && sa[i-1] > len && sa[i-1] < n) || (sa[i-1] >= 0 && sa[i-1] < len && sa[i] > len && sa[i] < n)判断是否取height[i],取出最大值。


    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 100000 * 2 + 10;
    
    char s[maxn], s2[maxn];
    int sa[maxn], t1[maxn], t2[maxn], c[maxn], rak[maxn], height[maxn], n;
    
    void build_sa(int m){
        int i, *x = t1, *y = t2;
        //基数排序
        for(i = 0; i < m; i++) c[i] = 0;
        for(i = 0; i < n; i++) c[x[i] = s[i]]++;
        for(i = 1; i < m; i++) c[i] += c[i-1];      //预留空位给比c[i]小的字符
        for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;        //从右往左,相等的从大号到小号
    
        for(int k = 1; k < n; k <<= 1){        //每次判断时已算完了长度为k的后缀
            int p = 0;
            //直接用sa数组排序第二关键字
            for(i = n-k; i < n; i++) y[p++] = i;
            for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k;       //刚才是sa[i]位,倍增后左移变位于sa[i]-k位
            //第二关键字排完序后已连成一条链,从端开始扫描就是
            //基数排序第一关键字
            for(i = 0; i < m; i++) c[i] = 0;
            for(i = 0; i < n; i++) c[x[y[i]]]++;
            for(i = 1; i < m; i++) c[i] += c[i-1];
            for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];      //从右往左扫描y[i]是第二关键字从大到小,它应放到第一关键字相同的最右
            //根据sa和y数组计算新的x数组
            swap(x, y);     //此时y数组变得没意义,但更新x数组又要用到原来的x数组,所以将原来的x数组存到y数组里
            p = 1;
            x[sa[0]] = 0;       //(以下类似于离散化)最小的那1位赋0,接着开始从小到大扫描
            for(i = 1; i < n; i++)
                x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k] ? p-1 : p++;
            if(p >= n) break;
            m = p;
        }
    }
    
    void getHeight(){
        int i, j, k = 0;
        for(i = 0; i < n; i++) rak[sa[i]] = i;
        height[0] = 0;
        for(i = 0; i < n; i++){
            if(!rak[i]) continue;       //注意判空!
            if(k) k--;      //height[rank[i]] >= height[rank[i-1]] - 1
            j = sa[rak[i]-1];       //等级比i小1的后缀编号为j
            while(s[i+k] == s[j+k]) k++;
            height[rak[i]] = k;
        }
    }
    
    void solve(){
        int len = strlen(s);
        s[len] = '$';
        s[len+1] = '';
        strcat(s, s2);
        n = strlen(s);
        build_sa(256);
        getHeight();
        int Max = -1, i;
        for(i = 1; i < n; i++) if((sa[i] >= 0 && sa[i] < len && sa[i-1] > len && sa[i-1] < n) || (sa[i-1] >= 0 && sa[i-1] < len && sa[i] > len && sa[i] < n)) Max = max(Max, height[i]);
        printf("%d
    ", Max);
    }
    
    int main()
    {
        while(scanf("%s%s", s, s2) == 2) solve();
        return 0;
    }
    


  • 相关阅读:
    P3302 [SDOI2013]森林
    P2542 [AHOI2005] 航线规划
    P5795 [THUSC2015]异或运算
    P3320 [SDOI2015]寻宝游戏
    P1963 [NOI2009] 变换序列
    一月练习日志
    计算几何全家桶
    bzoj1076: [SCOI2008]奖励关(期望dp+状压dp)
    bzoj3450 Easy(概率期望dp)
    Eclipse配置 自动补全功能 快捷键 alt+/
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3285629.html
Copyright © 2011-2022 走看看