• 后缀数组 POJ 3693 Maximum repetition substring


    题目链接

    题意:给定一个字符串,求重复次数最多的连续重复子串。

    分析:(论文上的分析)先穷举长度 L,然后求长度为 L 的子串最多能连续出现几次。首先连续出现 1 次是肯定可以的,所以这里只考虑至少 2 次的情况。假设在原字符串中连续出 现 2 次,记这个子字符串为 S,那么 S 肯定包括了字符 r[0], r[L], r[L*2], r[L*3], ……中的某相邻的两个。所以只须看字符 r[L*i]和 r[L*(i+1)]往前和 往后各能匹配到多远,记这个总长度为 K,那么这里连续出现了 K/L+1 次。最后 看最大值是多少。穷举长度 L 的时间是 n,每次计算的时间是 n/L。所以整个做法的时间复杂 度是 O(n/1+n/2+n/3+……+n/n)=O(nlogn)。

    因为枚举长度的同时不能枚举起点的位置,但是可以通过偏移(lcp%L)的距离来得到可能的最优起点。例如"dddcabcabcabcab"从红字与红字为开始点,长度为3的lcp为8("abcabcab"),偏移后从"dddcabcabcabcab"红字与红字开始,长度为3的lcp为9("cabcabcab")。题目要求次数最多,长度不限,字典序最小的,那么保存所有次数同样最大,不同长度的所有长度,在已经排好序的后缀从前往后匹配就行了。代码实现有诸多细节之处,另外数据水,不保证没有bug。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    
    const int N = 1e5 + 5;
    const int D = 20;
    char s[N];
    int sa[N], rank[N], height[N];
    int ws[N], wa[N], wb[N];
    int dp[N][D];
    int mlen[N];
    
    bool cmp(int *r, int a, int b, int l) {
        return (r[a] == r[b] && r[a+l] == r[b+l]);
    }
    void DA(char *r, int n, int m = 128) {
        int i, j, p, *x = wa, *y = wb;
        for (i=0; i<m; ++i) ws[i] = 0;
        for (i=0; i<n; ++i) ws[x[i]=r[i]]++;
        for (i=1; i<m; ++i) ws[i] += ws[i-1];
        for (i=n-1; i>=0; --i) sa[--ws[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<m; ++i) ws[i] = 0;
            for (i=0; i<n; ++i) ws[x[y[i]]]++;
            for (i=1; i<m; ++i) ws[i] += ws[i-1];
            for (i=n-1; i>=0; --i) sa[--ws[x[y[i]]]] = y[i];
            std::swap (x, y);
            p = 1; x[sa[0]] = 0;
            for (i=1; i<n; ++i) {
                x[sa[i]] = cmp (y, sa[i-1], sa[i], j) ? p - 1 : p++;
            }
        }
    }
    void calc_height(char *r, int *sa, int n) {
        int i, j, k = 0;
        for (i=1; i<=n; ++i) rank[sa[i]] = i;
        for (i=0; i<n; ++i) {
            if (k) k--;
            j = sa[rank[i]-1];
            while (r[i+k] == r[j+k]) k++;
            //其实并没有计算height[n]
            height[rank[i]] = k;
        }
    }
    
    int query_RMQ(int l, int r) {
        l = rank[l]; r = rank[r];
        if (l > r) {
            std::swap (l, r);
        }
        l++;
        int k = 0; while (1<<(k+1) <= r - l + 1) k++;
        return std::min (dp[l][k], dp[r-(1<<k)+1][k]);
    }
    void init_RMQ(int n) {
        //height[0]=lcp (suffix (sa[0], sa[0-1]));没有意义
        for (int i=1; i<=n; ++i) {
            dp[i][0] = height[i];
        }
        for (int j=1; (1<<j)<=n; j++) {
            for (int i=1; i+(1<<j)-1<n; ++i) {
                //与之对应,从height[1]开始
                dp[i][j] = std::min (dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    
    int main() {
        int cas = 0;
        while (scanf ("%s", s) == 1) {
            if (strcmp (s, "#") == 0) {
                break;
            }
            int n = strlen (s);
            DA (s, n + 1);
            calc_height (s, sa, n);
            init_RMQ (n);
            int best = -1, tot = 0;
            for (int l=1; l<=n; ++l) {
                for (int i=0; i+l<n; i+=l) {
                    int lcp = query_RMQ (i, i + l);
                    int m = l - lcp % l;
                    if (i - m >= 0 && lcp % l) {
                        lcp = std::max (lcp, query_RMQ (i - m, i - m + l));
                    }
                    int t = lcp / l + 1;
                    if (best < t) {
                        best = t;
                        tot = 0;
                        mlen[tot++] = l;
                    } else if (best == t && mlen[tot-1] != l) {
                        mlen[tot++] = l;
                    }
                }
            }
            //best: 重复次数 mlen: 每段长度
            int len = -1, from = 0;
            for (int i=1; i<=n && len==-1; ++i) {
                for (int j=0; j<tot; ++j) {
                    int l = mlen[j];
                    if (sa[i] + l > n) {
                        continue;
                    }
                    int lcp = query_RMQ (sa[i], sa[i] + l);
                    if (lcp >= (best - 1) * l) {
                        len = l; from = sa[i];
                        break;
                    }
                }
            }
            printf ("Case %d: ", ++cas);
            int L = len * best; //当字符串只有一个时,L=-1*-1=1, from=0, 输出s[0]
            for (int j=0, i=from; j<L; ++i, ++j) {
                printf ("%c", s[i]);
            }
            puts ("");
        }
        return 0;
    }
    

      

  • 相关阅读:
    vite启用host代理,自动无限刷新问题
    机器人语音交互
    让or使用索引
    leetcode 77. Combinations 组合(中等)
    leetcode 257. Binary Tree Paths 二叉树的所有路径(简单)
    leetcode 934. Shortest Bridge 最短的桥(中等)
    为什么 SQL 语句使用了索引,但却还是慢查询?
    leetcode 47. Permutations II 全排列 II(中等)
    leetcode 79. Word Search 单词搜索
    leetcode 126. Word Ladder II 单词接龙 II(困难)
  • 原文地址:https://www.cnblogs.com/Running-Time/p/5450477.html
Copyright © 2020-2023  润新知