zoukankan      html  css  js  c++  java
  • [bzoj1717][Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组_二分答案

    Milk Patterns 产奶的模式 bzoj-1717 Usaco-2006 Dec

    题目大意:给定一个字符串,求最长的至少出现了$k$次的子串长度。

    注释:$1le nle 2cdot 10^4$,$2le kle n$。


    想法:不难想到二分答案,现在我们考虑如何验证。

    这里就是后缀数组的一个妙用了。

    我们对原串建立后缀数组,观察$ht$数组。

    考虑当前二分出来的$mid$。如果有至少连续$k$的$ht$值都不小于$mid$,那么$k$就是合法的。

    故此我们直接扫$ht$数组看看最长的连续比$mid$大的$ht$有多少个即可。

    Code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1000010 
    using namespace std;
    int wv[N],wa[N],sa[N],Ws[N],wb[N],t[N],rk[N],ht[N],r[N];
    int n,m=1000009;
    inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}
    int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;}
    void build_sa()
    {
    	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;i--) sa[--Ws[x[i]]]=i;
    	for(p=j=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>=0) 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;i--) sa[--Ws[wv[i]]]=y[i];
    		for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++)
    		{
    			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i-1]+j]==y[sa[i]+j]) x[sa[i]]=p-1;
    			else x[sa[i]]=p++;
    		}
    	}
    	for(i=1;i<n;i++) rk[sa[i]]=i;
    	for(i=p=0;i<n-1;ht[rk[i++]]=p)
    		for(p?p--:0,j=sa[rk[i]-1];r[i+p]==r[j+p];p++);
    }
    template <typename T> void Max(T &x,T y) {x=max(x,y);}
    // inline void Max(int &x,int y) {x=max(x,y);}
    int check(int x)
    {
    	int re=-1,now=0;
    	for(int i=0;i<n;i++)
    	{
    		if(ht[i]>=x) now++;
    		else Max(re,now),now=1;
    	}
    	Max(re,now);
    	return re;
    }
    int main()
    {
    	n=rd();int k=rd(); for(int i=0;i<n;i++) r[i]=rd();
    	r[n++]=0; build_sa();
    	int l=0,r=n+1;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid)>=k) l=mid+1;
    		else r=mid;
    	}
    	l--;
    	cout << l << endl ;
    	return 0;
    }
    

    小结:后缀数组有很多特别有趣的妙用,要多积累啊!因为开$iostream$的缘故所以不能有变量叫做$ws$,我就叫$Ws$了……

  • 相关阅读:
    OAuth 2.0理论
    mvvm
    js中使用new所做的事情
    使用vue实现图片上传插件
    控制元素滚动位置
    float属性
    确定节点直接的关系
    react 开发中的问题简记
    深复制浅复制
    HTML和CSS实现常见的布局
  • 原文地址:https://www.cnblogs.com/ShuraK/p/10110574.html
Copyright © 2011-2022 走看看