• SPOJ 687 Repeats


    SPOJ_687

        这几天写了好多字的解题报告了,这次就偷懒一点吧,其实基本的思路在罗穗骞的论文里已经说得比较清楚了。

        其中值得一提的是,论文里说过这样一句话“所以只需看字符r[L*i]和r[L*(i+1)]往前和往后能匹配到多远”,对于往后能匹配到多远,这个直接根据最长公共前缀就能很容易得到,而对于往前能匹配到多远,我们当然可以一开始就把字符串反过来拼在后面,这样也能根据最长公共前缀来看往前能匹配到多远,但这样效率就比较低了。

        其实,当枚举到合适的子串长度时,我们在枚举r[L*i]和r[L*(i+1)]的过程中,必然可以出现r[L*i]在第一个循环节里,而r[L*(i+1)]在第二个循环节里的这种情况,如果此时r[L*i]是第一个循环节的首字符,这样直接用公共前缀k除以i并向下取整就可以得到最后结果。但如果r[L*i]如果不是首字符,这样算完之后结果就有可能偏小,因为r[L*i]前面可能还有少许字符也能看作是第一个循环节里的。

        于是,我们不妨先算一下,从r[L*i]开始,除匹配了k/i个循环节,还剩余了几个字符,剩余的自然是k%i个字符。如果说r[L*i]的前面还有i-k%i个字符完成比配的话,这样就相当于还可以再匹配出一个循环节,于是我们只要检查一下从r[L*i-(i-k%i)]和r[L*i-(i-k%i)+i]开始是否有i-k%i个字符能够完成匹配即可,也就是说去检查这两个后缀的最长公共前缀是否比i-k%i大即可。

        当然如果公共前缀不比i-k%i小,自然就不比i小,因为后面的字符都是可以匹配上的,所以我的程序里面就直接去看是否会比i小了。

    #include<stdio.h>
    #include<string.h>
    #define MAXD 100010
    char b[MAXD];
    int N, r[MAXD], sa[MAXD], rank[MAXD], height[MAXD], wa[MAXD], wb[MAXD], ws[MAXD], wv[MAXD];
    int best[20][MAXD], mm[MAXD];
    void init()
    {
    int i, j, k;
    scanf("%d", &N);
    for(i = 0; i < N; i ++)
    {
    scanf("%s", b + i);
    r[i] = b[i];
    }
    r[N] = 0;
    }
    int cmp(int *p, int x, int y, int l)
    {
    return p[x] == p[y] && p[x + l] == p[y + l];
    }
    void da(int n, int m)
    {
    int i, j, p, *x = wa, *y = wb, *t;
    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 = 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 ++)
    ws[i] = 0;
    for(i = 0; i < n; i ++)
    ++ ws[wv[i]];
    for(i = 1; i < m; i ++)
    ws[i] += ws[i - 1];
    for(i = n - 1; i >= 0; i --)
    sa[-- ws[wv[i]]] = y[i];
    for(t = x, x = y, y = t, x[sa[0]] = 0, i = p = 1; i < n; i ++)
    x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p ++;
    }
    }
    void calheight(int n)
    {
    int i, j, k = 1;
    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 ++);
    }
    void initRMQ(int n)
    {
    int i, j, x, y;
    for(mm[0] = -1, i = 1; i <= n; i ++)
    mm[i] = (i & (i - 1)) == 0 ? mm[i - 1] + 1 : mm[i - 1];
    for(i = 1; i <= n; i ++)
    best[0][i] = i;
    for(i = 1; i <= mm[n]; i ++)
    for(j = 1; j <= n - (1 << i) + 1; j ++)
    {
    x = best[i - 1][j];
    y = best[i - 1][j + (1 << (i - 1))];
    best[i][j] = height[x] < height[y] ? x : y;
    }
    }
    int askRMQ(int x, int y)
    {
    int t;
    t = mm[y - x + 1];
    y = y - (1 << t) + 1;
    x = best[t][x];
    y = best[t][y];
    return height[x] < height[y] ? height[x] : height[y];
    }
    int calculate(int x, int y)
    {
    int t;
    x = rank[x], y = rank[y];
    if(x > y)
    t = x, x = y, y = t;
    ++ x;
    return askRMQ(x, y);
    }
    void solve()
    {
    int i, j, k, x, y, ans, max = 0;
    da(N + 1, 128);
    calheight(N);
    initRMQ(N);
    for(i = 1; i < N; i ++)
    for(j = 0; j + i < N; j += i)
    {
    ans = calculate(j, j + i);
    k = j - (i - ans % i);
    ans = ans / i + 1;
    if(k >= 0 && calculate(k, k + i) >= i)
    ++ ans;
    if(ans > max)
    max = ans;
    }
    printf("%d\n", max);
    }
    int main()
    {
    int t;
    scanf("%d", &t);
    while(t --)
    {
    init();
    solve();
    }
    return 0;
    }


  • 相关阅读:
    java fx example
    JavaFX 2.0+ WebView /WebEngine render web page to an image
    剑指Offer面试题41(Java版):和为s的两个数字VS和为s的连续正数序列
    时间类(时间戳的各种转换成)
    Android系统各种类型的service刨根解读
    二叉树的建立基本操作(链表方式)(一)
    从头认识java-13.2 利用元组的方式返回多类型对象
    EA初步使用
    inline-block元素设置overflow:hidden属性导致相邻行内元素向下偏移
    下拉框与列表框
  • 原文地址:https://www.cnblogs.com/staginner/p/2340521.html
Copyright © 2020-2023  润新知