zoukankan      html  css  js  c++  java
  • SPOJ REPEATS Repeats (后缀数组 + RMQ:子串的最大循环节)题解

    题意:

    给定一个串(s)(s)必有一个最大循环节的连续子串(ss),问最大循环次数是多少

    思路:

    我们可以知道,如果一个长度为(L)的子串连续出现了两次及以上,那么必然会存在(s[0]、s[L]、s[2L] cdots s[L * k])中至少有两个连续的位置是相同的,然后看字母(s[L * i]和s[L * (i + 1)])往前往后最多能匹配多远,记住总长度(len),那么最大循环次数为((len / L) + 1)

    参考:

    SPOJ 687. Repeats(后缀数组)

    代码:

    #include<map>
    #include<set>
    #include<queue>
    #include<cmath>
    #include<stack>
    #include<ctime>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<sstream>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int maxn = 50000 + 5;
    const int INF = 0x3f3f3f3f;
    const ull seed = 11;
    const int MOD = 1e9 + 7;
    using namespace std;
    
    //下标从0开始
    int str[maxn];  //str[n]赋值一个最小值0,其他大于0
    int t1[maxn], t2[maxn], c[maxn];
    int sa[maxn];   //排名为i的后缀下标
    int rk[maxn];   //后缀下标为i的排名
    int height[maxn];   //sa[i]与sa[i - 1]的LCP
    int mm[maxn];
    int dp[maxn][30];
    bool cmp(int *r, int a, int b, int l){
        return r[a] == r[b] && r[a + l] == r[b + l];
    }
    void da(int *str, int n, int m){
        n++;
        int i, j, p, *x = t1, *y = t2;
        for(i = 0; i < m; i++) c[i] = 0;
        for(i = 0; i < n; i++) c[x[i] = str[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; j <= n; j <<= 1){
            p = 0;
            for(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++) 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];
            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++;
            if(p >= n) break;
            m = p;
        }
        int k = 0;
        n--;
        for(i = 0; i <= n; i++) rk[sa[i]] = i;
        for(i = 0; i < n; i++){
            if(k) k--;
            j = sa[rk[i] - 1];
            while(str[i + k] == str[j + k]) k++;
            height[rk[i]] = k;
        }
    }
    void initRMQ(int n){
        mm[0] = -1;
        for(int i = 1; i <= n; i++){
            dp[i][0] = height[i];
            mm[i] = ((i & (i - 1)) == 0)? mm[i - 1] + 1 : mm[i - 1];
        }
        for(int j = 1; j <= mm[n]; j++)
            for(int i = 1; i + (1 << j) - 1 <= n; i++)
                dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
    }
    int RMQ(int L, int R){
        int k = mm[R - L + 1];
        return min(dp[L][k], dp[R - (1 << k) + 1][k]);
    }
    int LCP(int i, int j){   //求后缀i和j的LCP最长公共前缀
        int L = rk[i], R = rk[j];
        if(L > R) swap(L, R);
        L++;
        return RMQ(L, R);
    }
    
    int main(){
        int T;
        scanf("%d", &T);
        while(T--){
            int n;
            scanf("%d", &n);
            for(int i = 0; i < n; i++){
                char ss[2];
                scanf("%s", ss);
                str[i] = ss[0] - 'a' + 1;
            }
            str[n] = 0;
            da(str, n, 3);
            initRMQ(n);
            int ans = 1;
            for(int i = 1; i < n; i++){
                for(int j = 0; j + i < n; j += i){
                    int len = LCP(j, j + i);
                    int times = len / i + 1;
                    int pos = j - (i - len % i);
                    if(pos >= 0){
                        len = LCP(pos, pos + i);
                        times = max(times, len / i + 1);
                    }
                    ans = max(ans, times);
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    安装armadillo
    windows sublime 2 破解
    ubuntu10.04安装有线网卡驱动
    x250装无线网卡驱动ubuntu
    main restricted universe muitiverse
    apt-get error
    新系統必須安裝的軟件列表
    更新ubuntu軟件源爲阿里雲腳本
    轉載:让重定向>,>>具有root权限
    margin的相关属性:
  • 原文地址:https://www.cnblogs.com/KirinSB/p/11262768.html
Copyright © 2011-2022 走看看