POJ 3261 字符串上的k次覆盖问题

题目大意:

给定一个数组,求一个最大的长度的子串至少出现过k次

 

一个子串出现多次,也就是说必然存在2个子串间的前缀长度为所求的值

通过二分答案,通过线性扫一遍,去判断出现次数,也就是说每次遇见一个height[i] , 出现次数就加1,否则重置为1

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 using namespace std;
 5 const int N = 20010;
 6 int rank[N] , sa[N] , height[N];
 7 int wa[N] , wb[N] , wsf[N] , wv[N];
 8 int a[N];
 9 
10 int cmp(int *r , int a , int b , int l)
11 {
12     return r[a]==r[b] && r[a+l]==r[b+l];
13 }
14 
15 void getSa(int *r , int *sa , int n , int m)
16 {
17     int i,j,p;
18     int *x=wa , *y=wb , *t;
19     for(i=0 ; i<m ; i++) wsf[i]=0;
20     for(i=0 ; i<n ; i++) wsf[x[i]=r[i]]++;
21     for(i=1 ; i<m ; i++) wsf[i] += wsf[i-1];
22     for(i=n-1 ; i>=0 ; i--) sa[--wsf[x[i]]]=i;
23 
24     p=1;
25     for(j=1 ; p<n ; j*=2 , m=p){
26         for(p=0 , i=n-j ; i<n ; i++) y[p++]=i;
27         for(i=0 ; i<n ; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
28 
29         for(i=0 ; i<n ; i++) wv[i]=x[y[i]];
30         for(i=0 ; i<m ; i++) wsf[i]=0;
31         for(i=0 ; i<n ; i++) wsf[wv[i]]++;
32         for(i=1 ; i<m ; i++) wsf[i]+=wsf[i-1];
33         for(i=n-1 ; i>=0 ; i--) sa[--wsf[wv[i]]]=y[i];
34 
35         t=x,x=y,y=t;
36         x[sa[0]]=0;
37         for(p=1 , i=1 ; i<n ; i++)
38             x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j)?p-1:p++;
39     }
40     return ;
41 }
42 
43 void getHeight(int *r , int *sa , int n)
44 {
45     for(int i=1 ; i<=n ; i++) rank[sa[i]]=i;
46     int k=0;
47     int j;
48     for(int i=0 ; i<n ; height[rank[i++]]=k)
49         for(k?k--:0 , j=sa[rank[i]-1] ; r[i+k]==r[j+k] ; k++);
50     return;
51 }
52 
53 bool check(int m , int n , int k)
54 {
55     int cnt=1;
56     for(int i=1 ; i<=n ; i++){
57         if(height[i]>=m){
58             cnt++;
59          //   cout<<"here: "<<m<<" "<<sa[i]<<" "<<sa[i-1]<<endl;
60             if(cnt>=k) return true;
61         }
62         else {cnt=1;if(cnt>=k) return true;}
63     }
64     return false;
65 }
66 
67 int main()
68 {
69   //  freopen("a.in" , "r" , stdin);
70     int n,k;
71     while(~scanf("%d%d" , &n , &k))
72     {
73         int maxn =0 ;
74         for(int i=0 ; i<n ; i++){
75             scanf("%d" , a+i);
76             a[i]++;
77             maxn = max(maxn , a[i]);
78         }
79         a[n]=0;
80         getSa(a , sa , n+1 , maxn+1);
81         getHeight(a , sa , n);
82 
83         int l=0 , r=n , ans=0;
84         while(l <= r){
85             int m=(l+r)>>1;
86             if(check(m,n,k)){
87                 ans = m;
88                 l=m+1;
89             }else r=m-1;
90         }
91         printf("%d\n" , ans);
92     }
93     return 0;
94 }

 

转载于:https://www.cnblogs.com/CSU3901130321/p/4388958.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值