poj 3693 Maximum repetition substring 重复次数最多的连续子串

题目链接

题意

对于任意的字符串,定义它的 重复次数 为:它最多可被划分成的完全相同的子串个数。例如:ababab 的重复次数为3,ababa 的重复次数为1.

现给定一字符串,求它的一个子串,其重复次数取到最大值,且字典序取到最小值。

思路

参考 hzwer.

首先,重复次数显然至少为\(1\),所以下面只考虑重复次数\(\geq 2\)的情况。

首先枚举子串长度\(L\)。对于长度为\(L\)的子串,其重复次数至少为\(2\),意味着它的其中两个重复部分必为\(s[0],s[L],s[2L],...\)中相邻的两个。

所以只需枚举\(i\),看\(s[i*L]\)和\(s[(i+1)*L]\)向左\((le)\)向右\((ri)\)最多能匹配到多远,记总长度为\(k\),则重复次数\(=k/L+1\).

至于开头位置,则落在\(i*L-le\)和\(i*L-le+(le+ri)\%k\)之间,取字典序最小者。

对于所有重复次数相同的子串,要使其字典序取到最小值,即取\(rank\)值最小者。

上述步骤中,看最长匹配多远(即看最长公共前缀)与取rank最小值均借助\(ST\)表实现。

Code

#include <stdio.h>
#include <iostream>
#include <string.h>
#define maxn 100010
using namespace std;
typedef long long LL;
int wa[maxn], wb[maxn], wv[maxn], wt[maxn], r1[maxn], r2[maxn],
    h1[maxn], h2[maxn], rk1[maxn], rk2[maxn], sa[maxn], n, kas, bin[20], Log[maxn];
struct rmqNode { int val, p; } mn[maxn][20];
struct node { int val, pos, len; };
int mn1[maxn][20], mn2[maxn][20];
char s[maxn];
void rmqInit(int n) {
    Log[0] = -1; bin[0] = 1;
    for (int i = 1; i < 20; ++i) bin[i] = bin[i-1] << 1;
    for (int i = 1; i <= n; ++i) Log[i] = Log[i>>1] + 1;
}
bool cmp(int* r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; }
void init(int* r, int* rk, int* h, int n, int m) {
    int* x=wa, *y=wb, *t, i, j, p;
    for (i = 0; i < m; ++i) wt[i] = 0;
    for (i = 0; i < n; ++i) ++wt[x[i] = r[i]];
    for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
    for (i = n-1; i >= 0; --i) sa[--wt[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) wt[i] = 0;
        for (i = 0; i < n; ++i) ++wt[wv[i]];
        for (i = 1; i < m; ++i) wt[i] += wt[i - 1];
        for (i = n-1; i >= 0; --i) sa[--wt[wv[i]]] = y[i];

        t = x, x = y, y = t, x[sa[0]] = 0;
        for (p = 1, i = 1; i < n; ++i) x[sa[i]] = cmp(y, sa[i], sa[i-1], j) ? p - 1 : p++;
    }

    for (i = 0; i < n; ++i) rk[sa[i]] = i;
    int k = 0;
    for (i = 0; i < n - 1; h[rk[i++]] = k) {
        for (k = k ? --k : 0, j = sa[rk[i] - 1]; r[i+k] == r[j+k]; ++k);
    }
}
void rmq1(int* a, int (*mn)[20], int n) {
    for (int i = 1; i <= n; ++i) mn[i][0] = a[i];
    for (int j = 1; bin[j] <= n; ++j) {
        for (int i = 1; i+bin[j-1]-1 <= n; ++i) {
            mn[i][j] = min(mn[i][j-1], mn[i+bin[j-1]][j-1]);
        }
    }
}
void rmq2(int* a, rmqNode (*mn)[20], int n) {
    for (int i = 1; i <= n; ++i) mn[i][0] = {a[i-1], i};
    for (int j = 1; bin[j] <= n; ++j) {
        for (int i = 1; i+bin[j-1]-1 <= n; ++i) {
            if (mn[i][j-1].val <= mn[i+bin[j-1]][j-1].val) mn[i][j] = mn[i][j-1];
            else mn[i][j] = mn[i+bin[j-1]][j-1];
        }
    }
}
int query1(int (*mn)[20], int l, int r) {
    int k = Log[r-l+1];
    return min(mn[l][k], mn[r-bin[k]+1][k]);
}
int query2(rmqNode (*mn)[20], int l, int r) {
    int k = Log[r-l+1];
    return mn[l][k].val < mn[r-bin[k]+1][k].val ? mn[l][k].p : mn[r-bin[k]+1][k].p;
}
int match(int* rk, int (*mn)[20], int p, int len) {
    int rk1 = rk[p], rk2 = rk[p+len];
    if (rk1 > rk2) swap(rk1, rk2);
    return query1(mn, rk1+1, rk2);
}
void work() {
    int tot1=0, tot2=0, m=0, len=strlen(s);
    for (int i = 0; i < len; ++i) m = max(r1[tot1++] = s[i], m); r1[tot1++] = 0;
    for (int i = len-1; i >= 0; --i) r2[tot2++] = s[i]; r2[tot2++] = 0;
    rmqInit(len);
    init(r1, rk1, h1, tot1, ++m);
    init(r2, rk2, h2, tot2, m);
    rmq1(h1, mn1, len);
    rmq1(h2, mn2, len);
    rmq2(rk1, mn, len);

    node ans = {1,0,0};
    for (int l = 1; l <= len; ++l) {
        int lim = len / l, upp;
        if (ans.val >= lim+1) break;
        if (len % l) upp = lim; else upp = lim-1;
        for (int i = 0; i < upp; ++i) {
            int ri = match(rk1, mn1, i*l, l),
                le = i ? match(rk2, mn2, len-(i+1)*l, l) : 0,
                k = le + ri;
            int cnt = k/l + 1;
            if (cnt >= ans.val) {
                int l1 = i*l-le, l2 = l1+k%l,
                    p = query2(mn, l1+1, l2+1)-1;
                if (cnt > ans.val || rk1[p]<rk1[ans.pos]) ans = {cnt, p, cnt*l};
            }
        }
    }
    printf("Case %d: ", ++kas);
    if (ans.val == 1) {
        char ch='z'+1; for (int i = 0; s[i]; ++i) ch = min(ch, s[i]);
        putchar(ch); puts("");
    }
    else {
        s[ans.pos+ans.len] = '\0';
        puts(s+ans.pos);
    }
}
int main() {
    while (scanf("%s", s) != EOF && s[0]!='#') work();
    return 0;
}

原文地址:https://www.cnblogs.com/kkkkahlua/p/8443887.html

时间: 2024-10-08 15:26:08

poj 3693 Maximum repetition substring 重复次数最多的连续子串的相关文章

【POJ 3693】Maximum repetition substring 重复次数最多的连续重复子串

后缀数组的论文里的例题,论文里的题解并没有看懂,,, 求一个重复次数最多的连续重复子串,又因为要找最靠前的,所以扫的时候记录最大的重复次数为$ans$,扫完后再后从头暴力扫到尾找重复次数为$ans$的第一个子串的开头,break输出就可以了 #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1000

poj 3693 后缀数组求重复次数最多的连续重复子串

#include<iostream> #include<cstring> #include<set> #include<map> #include<cmath> #include<stack> #include<queue> #include<deque> #include<list> #include<algorithm> #include<stdio.h> #includ

POJ 3693 Maximum repetition substring (寻找重复次数最多的连续子串)

Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9083   Accepted: 2782 Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same conse

POJ 3693 Maximum repetition substring (后缀数组)

题目大意: 求出字典序最小,重复次数最多,的子串. 思路分析: RMQ + height 数组可以求出任意两个后缀的lcp 我们枚举答案字符串的重复的长度. 如果这个字符串的长度为 l ,而且这个字符串出现过两次或两次以上 那么你会发现在原串中  str[0] str[l] str[2*l] ....肯定有相邻的两个被包含在重复的串中. 我们求出这两个相邻的后缀的lcp 我们上面仅仅说的是被包含在重复的串中,但并不一定就是以 str[0], str[l],str[2*l]....为起点的. 那我

POJ 3693 Maximum repetition substring(后缀数组神题)

POJ 3693 Maximum repetition substring 题目链接 题意:给定一个字符串,求出其子串中,重复次数最多的串,如果有相同的,输出字典序最小的 思路:枚举长度l,把字符串按l分段,这样对于长度为l的字符串,肯定会包含一个分段位置,这样一来就可以在每个分段位置,往后做一次lcp,求出最大匹配长度,然后如果匹配长度有剩余,看剩余多少,就往前多少位置再做一次lcp,如果匹配出来长度更长,匹配次数就加1,这样就可以枚举过程中保存下答案了 这样问题还有字典序的问题,这个完全可以

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>

POJ 3693 Maximum repetition substring(后缀数组+RMQ)

Maximum repetition substring The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa&quo

POJ - 3693 Maximum repetition substring(后缀数组求重复次数最多的连续重复子串)

Description The repetition number of a string is defined as the maximum number R such that the string can be partitioned into R same consecutive substrings. For example, the repetition number of "ababab" is 3 and "ababa" is 1. Given a

POJ 3693 Maximum repetition substring(后缀数组+ST表)

[题目链接] poj.org/problem?id=3693 [题目大意] 求一个串重复次数最多的连续重复子串并输出,要求字典序最小. [题解] 考虑错位匹配,设重复部分长度为l,记s[i]和s[i+l]前缀匹配得到的最长长度为r, 枚举所有的l和i,得到r,那么答案就是r/l+1的最大值. 计算任意后缀的最长公共前缀可以利用后缀数组+ST表来解决, 两个后缀的最长公共前缀就是他们名次之间的h数组的最小值. 显然,枚举i和l的复杂度达到了O(n2),是没有办法完成统计的, 我们发现每个区段只会存