[USACO06DEC] Milk Patterns

后缀数组求最长k次重复子串。

poj 3261 传送门

洛谷 P2852 传送门

求完height数组之后倍增答案。

check的时候枚举height数组。

如果连续cnt个height都大于等于某长度ans,且cnt不小于题目要求的k次重复,则ans为一个合法答案。

如果枚举过程中遇到了小于ans的height,则需要重置计数器cnt(初值为1)。

注意考虑每个数组分别需要开多大。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n,m,ans;
 7 int s[20005];
 8 int sa[20005],rk[20005];
 9 int h[1000005],tr[1000005];
10 
11 int cmp(int x,int y,int k)
12 {
13     if(x+k>n||y+k>n)return 0;
14     return rk[x]==rk[y]&&rk[x+k]==rk[y+k];
15 }
16 
17 void cal()
18 {
19     int i,cnt;
20     for(i=1;i<=n;i++)h[s[i]]++;
21     for(i=1,cnt=0;i<=1000000;i++)if(h[i])tr[i]=++cnt;
22     for(i=1;i<=1000000;i++)h[i]+=h[i-1];
23     for(i=1;i<=n;i++)rk[i]=tr[s[i]],sa[h[s[i]]--]=i;
24     for(int k=1;cnt!=n;k<<=1)
25     {
26         for(i=1;i<=n;i++)h[i]=0;
27         for(i=1;i<=n;i++)h[rk[i]]++;
28         for(i=1;i<=n;i++)h[i]+=h[i-1];
29         for(i=n;i;i--)if(sa[i]>k)tr[sa[i]-k]=h[rk[sa[i]-k]]--;
30         for(i=1;i<=k;i++)tr[n-i+1]=h[rk[n-i+1]]--;
31         for(i=1;i<=n;i++)sa[tr[i]]=i;
32         for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k)?cnt:++cnt;
33         for(i=1;i<=n;i++)rk[i]=tr[i];
34     }
35     for(i=1;i<=n;i++)h[i]=0;
36     for(i=1;i<=n;i++)
37     {
38         if(rk[i]==1)continue;
39         for(int j=max(1,h[rk[i-1]]-1);;j++)
40         {
41             if(s[i+j-1]==s[sa[rk[i]-1]+j-1])h[rk[i]]=j;
42             else break;
43         }
44     }
45 }
46 
47 int check(int k)
48 {
49     if(k>n)return 0;
50     int cnt=1;
51     for(int i=2;i<=n;i++)
52     {
53         if(h[i]>=k)cnt++;
54         else cnt=1;
55         if(cnt>=m)return 1;
56     }
57     return 0;
58 }
59 
60 int main()
61 {
62     scanf("%d%d",&n,&m);
63     for(int i=1;i<=n;i++)scanf("%d",&s[i]);
64     cal();
65     for(int i=20;i>=0;i--)
66         if(check(ans|(1<<i)))ans|=(1<<i);
67     printf("%d",ans);
68     return 0;
69 }

 

转载于:https://www.cnblogs.com/eternhope/p/9715035.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值