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 }
时间: 2024-10-16 18:05:53

POJ 3261 字符串上的k次覆盖问题的相关文章

POJ 3261 可重叠的 k 次最长重复子串【后缀数组】

这也是一道例题 给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠.算法分析:这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组.不同的是,这里要判断的是有没有一个组的后缀个数不小于 k.如果有,那么存在k 个相同的子串满足条件,否则不存在.这个做法的时间复杂度为 O(nlogn). Source Code: //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include

Milk Patterns - poj 3261 (求重复k次的最长子串)

题目大意:给你一个数组,求这个数组里面至少重复k次的子串. 分析:后缀数组的练手题目...不过给的数字比较大,可以先离散化处理一下即可. 代码如下: =============================================================================================================================== #include<stdio.h> #include<string.h> #in

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

poj 3261 后缀数组 找反复出现k次的子串(子串能够重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 可是大部分时间是在调试代码.由于模板的全局变量用混了,而自己又忘了.,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个事实上跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函

POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)

Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14094   Accepted: 6244 Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation,

POJ 3801 有上下界最小流

1: /** 2: POJ 3801 有上下界的最小流 3: 4: 1.对supersrc到supersink 求一次最大流,记为f1.(在有源汇的情况下,先使整个网络趋向必须边尽量满足的情况) 5: 2.添加一条边sink -> src,流量上限为INF,这条边记为p.(构造无源汇网络) 6: 3.对supersrc到supersink再次求最大流,记为f2,这里判断是否为可行流.(要判断可行,必须先构造无源汇网络流,因此要再次求最大流) 7: 8: 此网络流的最小流即为 sink -> s

HDU 4862 Jump(最大k路径覆盖 费用流)(待续)

题意:一个n*m的矩阵,需要遍历所有点,从起点出发每次只可向右或向下跳,若到达位置的数字与上一步的数字相同,则获得该数字大小的能量: 否则消耗能量:哈密顿距离减1:求可获得的最大能量: 思路:网络流之最大k路径覆盖. 源点向n*m各点建流量为1,费用为0的边: n*m各点向汇点建流量为1,费用为0的边: 新增一个起点: 源点向起点建流量为k,费用为0的边:起点向各点建流量1,费用为0的边: n*m各点间建边: 建好图后跑最小费用最大流,如果满流则存在解,否则不存在:最小费用的相反数就是所能够获得

hdu 4862 KM算法 最小K路径覆盖的模型

http://acm.hdu.edu.cn/showproblem.php?pid=4862 选t<=k次,t条路要经过所有的点一次并且仅仅一次, 建图是问题: 我自己最初就把n*m 个点分别放入X集合以及Y集合,再求最优匹配,然后连样例都过不了,而且其实当时解释不了什么情况下不能得到结果,因为k此这个条件相当于没用上... 建图方法: 1.X集合和Y集合都放入n*m+k个点,X中前n*m个点和Y中前n*m个点之间,如果格子里的值相等,权就是(收益-耗费),不等就是(-耗费),因为要的是最大收益

HDU 4862 Jump(最小K路径覆盖)

输入一个n×m网格图,每个结点的值为0-9,可以从任意点出发不超过k次,走完每个点且仅访问每个结点一次,问最终的能量最大值.不可全部走完的情况输出-1. 初始能量为0. 而结点(x,y)可以跳跃到结点(x,y+dy)或(x+dx,y).消耗能量为跳跃前后结点的曼哈顿距离 - 1 .若跳跃前后的结点的值相等,能量加上那个值. 具体建图可以参考这里http://blog.sina.com.cn/s/blog_6bddecdc0102uy9g.html 最小K路径覆盖其实在之前是见过的打过的,不过这次