• 产奶模式「USACO 2006」


    【题目描述】
    农夫John发现他的奶牛产奶的质量一直在变动。经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠。我们称之为一个“模式”。 John的牛奶按质量可以被赋予一个(0)(1000000)之间的数。并且John记录了(N(1le Nle 20000))天的牛奶质量值。他想知道最长的出现了至少(K(2le Kle 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
    ", ans);
    	return 0;
    } 
    
  • 相关阅读:
    CSS练习
    大作业“抽屉页面”html+css
    html练习代码
    协程-基于TCP的高并发通信
    协程-爬虫示例
    互斥锁,递归锁,信号量
    三层架构(我的理解及详细分析)
    递归算法经典实例小结(C#实现)
    使用XmlWriter写Xml
    使用XmlReader读Xml
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM28.html
Copyright © 2020-2023  润新知