产奶模式「USACO 2006」

【题目描述】
农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个\(0\)到\(1000000\)之间的数。并且John记录了\(N(1\le N\le 20000)\)天的牛奶质量值。他想知道最长的出现了至少\(K(2\le K\le N)\)次的模式的长度。比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次。当\(K=2\)时,这个长度为4。

【输入格式】
Line 1: 两个整数 N,K。
Lines 2..N+1: 每行一个整数表示当天的质量值。

【输出格式】
Line 1: 一个整数:N天中最长的出现了至少K次的模式的长度

题解

后缀数组+LCP(最长公共前缀)
这个组合的好处是什么呢 一个字符串中的每个子串都必然是一个后缀的前缀

定义suf(i)表示s[i~n]
定义height[i]表示suf(sa[i-1])与suf(sa[i])的最长公共前缀 即排名为i的后缀与他排名前一个的后缀的LCP
关于height数组这里只给出求法 具体证明请自行百度

void geth() {
    int p = 0;
    for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
    for (int i = 1; i <= n; i++) {
        if (p) p--; int j = sa[rnk[i]-1];
        while (s[i+p] == s[j+p]) p++;
        height[rnk[i]] = p;
    }
}

引理1
\(LCP(suf(x), suf(y)) = min_{i=rnk[x]+1}^{rnk[y]} height[i]\)
证明略(其实是懒)

假设现在要找一个最长的子串 使得这个子串在原串中出现至少2次(可重叠)
只需要找出height数组的最大值即是答案
证明:假设\(LCP(suf(x), suf(y))=k\) 不妨设rnk[x]<rnk[y] 那么由引理1 一定有\(LCP(suf(x), suf(sa[rnk[x]+1]))=height[rnk[x]+1]\ge k\)
(sa[rnk[x]+1]即排名在suf(x)后一名的后缀)

那么如果出现至少3次呢 同理可得 如果存在一对连续的height[i],height[i+1]均大于等于m 就存在一个至少出现3次的长为m的子串

得出一般性结论 如果能找到一段连续k-1个height[i+1]~height[i+k-1]均大于等于m 那么就存在一个出现至少k次的长为m的子串
由引理1 这段区间内的k个后缀suf(sa[i])~suf(sa[i+k-1])必然是两两有长度至少为m的LCP

对于这题 可以二分答案mid 然后判断是否存在一段k-1个height全部大于等于mid
读入数据可以离散化一下 不离散化也无所谓 时间复杂度\(O(n\ log\ n)\)

扩展: 求一个最长子串使得这个子串在原串中出现至少2次(不可重叠)
方法: 二分答案 找出每一段 连续一些height全部大于二分值mid的区间 形如height[l+1]~height[r]
如果\(max_{i=l}^{r}sa[i]-min_{i=l}^{r}sa[i]\ge mid\) 则必定存在不重叠的长为mid的相同子串(自行理解一下)

代码

#include <bits/stdc++.h>
#define mx 1000005
using namespace std;

int s[mx], srt[mx], mxx;
int n, m, k, ans;
int sa[mx], sa2[mx], rnk[mx], key[mx], sum[mx], height[mx];

inline bool check(int *num, int a, int b, int l) { return num[a] == num[b] && num[a+l] == num[b+l]; }

inline void suffix() {
    int i, j, p; int *_rnk = rnk, *_sa2 = sa2, *tmp;
    for (i = 1; i <= m; i++) sum[i] = 0;
    for (i = 1; i <= n; i++) sum[_rnk[i]=s[i]]++;
    for (i = 2; i <= m; i++) sum[i] += sum[i-1];
    for (i = n; i >= 1; i--) sa[sum[_rnk[i]]--] = i;
    for (j = 1; p <= n; j <<= 1, m = p) {
        p = 0;
        for (i = n - j + 1; i <= n; i++) _sa2[++p] = i;
        for (i = 1; i <= n; i++) if (sa[i] > j) _sa2[++p] = sa[i] - j;
        for (i = 1; i <= n; i++) key[i] = _rnk[_sa2[i]];
        for (i = 1; i <= m; i++) sum[i] = 0;
        for (i = 1; i <= n; i++) sum[key[i]]++;
        for (i = 2; i <= m; i++) sum[i] += sum[i-1];
        for (i = n; i >= 1; i--) sa[sum[key[i]]--] = _sa2[i];
        for (tmp = _rnk, _rnk = _sa2, _sa2 = tmp, p = 2, _rnk[sa[1]] = 1, i = 2; i <= n; i++) {
            _rnk[sa[i]] = check(_sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
        }
    }
} 

inline void geth() {
    int p = 0;
    for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
    for (int i = 1; i <= n; i++) {
        if (p) p--; int j = sa[rnk[i]-1];
        while (s[i+p] == s[j+p]) p++;
        height[rnk[i]] = p;
    }
}

inline bool check(int mid) {
    int p = 0;
    for (int i = 2; i <= n; i++) {
        if (height[i] >= mid) p++;
        else p = 0;
        if (p >= k - 1) return 1;
    }
    return 0;
}

int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i]); srt[i] = s[i];
    }
    sort(srt+1, srt+n+1);
    mxx = unique(srt+1, srt+n+1)-srt-1;
    for (int i = 1; i <= n; i++) {
        s[i] = lower_bound(srt+1, srt+mxx+1, s[i]) - srt;
    }
    m = 1000000;
    suffix(); geth();
    int l = 1, r = n, mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid; l = mid + 1;
        } else r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
} 

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM28.html

时间: 2024-08-30 13:20:31

产奶模式「USACO 2006」的相关文章

护城河的挖掘「USACO 2006」

题意 凸包模板,给定平面上点集,求包含所有点的凸包周长最小值. 思路 使用\(Graham\)扫描法解决. 考虑将最左下的点设为原点(事实上任意点均可作为原点),然后其余各点根据斜率排序. 对于每一个节点,我们考虑加入它是否会与已有的边点构成内凹,如果会,那么放弃已有边点. 显然我们可以通过维护一个单调栈完成该过程. 代码 #include <bits/stdc++.h> using namespace std; namespace StandardIO { template<typen

BZOJ 1717 Usaco 2006 Dec 产奶模式

1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1557  Solved: 847[Submit][Status][Discuss] Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个"模式". John的牛奶按质量可以被赋予一个0到10000

队列变换「USACO 2007」

[题目描述] FJ打算带他的\(N(1 \leq N \leq 30,000)\)头奶牛去参加一年一度的"全美农场主大奖赛".在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过. 今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比如说,如果FJ带去的奶牛依次为Bessie.Sylvia.Dora,登记人员就把这支队伍登记为BSD).登记结束后,组委会将所有队伍的登记名称按字

[BZOJ1717][Usaco2006 Dec]Milk Patterns 产奶的模式

1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 1297  Solved: 705 [Submit][Status][Discuss] Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个"模式". John的牛奶按质量可以被赋予一个0到100

BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 [后缀数组]

1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1017  Solved: 561[Submit][Status][Discuss] Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个“模式”. John的牛奶按质量可以被赋予一个0到1000000之间的

王堅:「資料」改變了商業模式,運算能力決定企業的競爭力

阿里巴巴集團技術委員會主席.阿里巴巴的雲端建立者,王堅博士於上週來到台灣,出席了阿里巴巴針對台灣創業者舉辦的一場大會時,發表了他對於雲端運算.大數據以及人工智慧的一些看法以及建議. 由於是針對創業者的場合,王堅針對現在創業者最注意的四個趨勢:網際網路.大數據.雲端運算和人工智慧提出了建議.王堅表示,當初阿里巴巴談電子商務的時候,並不是大家想的簡單的把商店搬到網路上如此而已,而是從「相信網際網路是未來商業的基礎設施」這個基本觀念出發.而現在,你可以看到當初有這樣想法的網路公司,現在都已經成長為科技

BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )

二分答案m, 后缀数组求出height数组后分组来判断. ------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; const int maxn = 20009; struct HASH { int id[maxn], N; HASH() { N = 0; } inline void work() { sort(id, id + N); N

【BZOJ-1717】Milk Patterns产奶的模式 后缀数组

1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 881  Solved: 480[Submit][Status][Discuss] Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个“模式”. John的牛奶按质量可以被赋予一个0到1000000之间的数

《iOS「通告机制」及由其引出的对「架构模式」、「设计模式」的理解

说明:为了区别「本地通知」与「推送通知」这两种iOS中提醒用户,可见的「通知」,本文所将Notification翻译为「通告」.它们的详细区别,可参考<iOS开发系列--通知与消息机制>一文. 实践遇到的问题: 最近在维护公司的一个项目中,遇到这样一个报错:-[GlobalManager addAlbum:]: unrecognized selector sent to instance 经排查,原因如下:以前同事在利用「通告机制」在GlobalManager类中把「自己/self」注册为「观