题意:给定一个字符串,求至少出现k 次的最长重复子串,这k 个子串可以重叠。

分析:经典的后缀数组求解题:先二分答案,然后将后缀分成若干组。这里要判断的是有没有一个组的符合要求的后缀个数(height[i] >= mid)不小于k。如果有,那么存在
k 个相同的子串满足条件,否则不存在。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 22222
#define M 1111111
#define INF 0x7FFFFFFF
/****后缀数组模版****/
#define F(x)((x)/3+((x)%3==1?0:tb)) //F(x)求出原字符串的suffix(x)在新的字符串中的起始位置
#define G(x)((x)<tb?(x)*3+1:((x)-tb)*3+2) //G(x)是计算新字符串的suffix(x)在原字符串中的位置,和F(x)为互逆运算
int wa[N],wb[N],wv[N],WS[M];
int sa[N*3] ; //第i小的后缀,起始位置在源字符串的位置
int rank1[N],height[N]; //rank 以i为起始位置的后缀在后缀排列中的名次
int r[N*3]; //如果输入是字符串,承接字符串,用来计算 int c0(int *r,int a,int b) {
return r[a]==r[b] && r[a+1]==r[b+1] && r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b) {
if(k==2)
return r[a]<r[b] || ( r[a]==r[b] && c12(1,r,a+1,b+1) );
else
return r[a]<r[b] || ( r[a]==r[b] && wv[a+1]<wv[b+1] );
}
void sort(int *r,int *a,int *b,int n,int m) {
int i;
for(i=0; i<n; i++)
wv[i]=r[a[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>=0; i--)
b[--WS[wv[i]]]=a[i];
return;
} //注意点:为了方便下面的递归处理,r数组和sa数组的大小都要是3*n
void dc3(int *r,int *sa,int n,int m) { //rn数组保存的是递归处理的新字符串,san数组是新字符串的sa
int i , j , *rn = r+n , *san = sa+n , ta = 0 ,tb = (n+1)/3 , tbc = 0 , p;
r[n] = r[n+1] = 0;
for(i=0; i<n; i++) {
if(i%3!=0)
wa[tbc++]=i; //tbc表示起始位置模3为1或2的后缀个数
}
sort(r+2,wa,wb,tbc,m);
sort(r+1,wb,wa,tbc,m);
sort(r,wa,wb,tbc,m);
for(p=1,rn[F(wb[0])]=0,i=1; i<tbc; i++)
rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
if(p<tbc)
dc3(rn,san,tbc,p);
else {
for(i=0; i<tbc; i++)
san[rn[i]]=i;
}
//对所有起始位置模3等于0的后缀排序
for(i=0; i<tbc; i++) {
if(san[i]<tb)
wb[ta++]=san[i]*3;
}
if(n%3==1) //n%3==1,要特殊处理suffix(n-1)
wb[ta++]=n-1;
sort(r,wb,wa,ta,m);
for(i=0; i<tbc; i++)
wv[wb[i] = G(san[i])]=i;
//合并所有后缀的排序结果,保存在sa数组中
for(i=0,j=0,p=0; i<ta&&j<tbc; p++)
sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
for(; i<ta; p++)
sa[p]=wa[i++];
for(; j<tbc; p++)
sa[p]=wb[j++];
return;
} //height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀
void calheight(int *r,int *sa,int n) {
int i,j,k=0;
for(i=1; i<=n; i++)
rank1[sa[i]]=i;
for(i=0; i<n; height[rank1[i++]]=k)
for(k?k--:0,j=sa[rank1[i]-1]; r[i+k]==r[j+k]; k++);
} bool judge(int mid,int n,int k) {
int cnt = 1;
for(int i=1; i<=n; i++) {
if(height[i] >= mid) {
cnt ++;
} else cnt = 1;
if(cnt >= k) return true;
}
return false;
} int main() {
int n,k;
cin >> n >> k;
for(int i=0; i<n; i++) {
scanf("%d",&r[i]);
r[i] ++;
}
r[n] = 0; //要保证结尾最小
dc3(r,sa,n+1,1000010);
calheight(r,sa,n);
int l=1, r=n,mid; //枚举长度
int ans = 0;
while(l <= r) {
mid = (l+r) >> 1;
if(judge(mid,n,k)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans << endl;
return 0;
}

因为m太大,而n只有2w,简单的离散化之后,基数排序效率提高,总效率也提高了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 22222
#define INF 0x7FFFFFFF
/****后缀数组模版****/
#define F(x)((x)/3+((x)%3==1?0:tb)) //F(x)求出原字符串的suffix(x)在新的字符串中的起始位置
#define G(x)((x)<tb?(x)*3+1:((x)-tb)*3+2) //G(x)是计算新字符串的suffix(x)在原字符串中的位置,和F(x)为互逆运算
int wa[N],wb[N],wv[N],WS[N];
int sa[N*3] ; //第i小的后缀,起始位置在源字符串的位置
int rank1[N],height[N]; //rank 以i为起始位置的后缀在后缀排列中的名次
int r[N*3]; //如果输入是字符串,承接字符串,用来计算 int c0(int *r,int a,int b) {
return r[a]==r[b] && r[a+1]==r[b+1] && r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b) {
if(k==2)
return r[a]<r[b] || ( r[a]==r[b] && c12(1,r,a+1,b+1) );
else
return r[a]<r[b] || ( r[a]==r[b] && wv[a+1]<wv[b+1] );
}
void sort(int *r,int *a,int *b,int n,int m) {
int i;
for(i=0; i<n; i++)
wv[i]=r[a[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>=0; i--)
b[--WS[wv[i]]]=a[i];
return;
} //注意点:为了方便下面的递归处理,r数组和sa数组的大小都要是3*n
void dc3(int *r,int *sa,int n,int m) { //rn数组保存的是递归处理的新字符串,san数组是新字符串的sa
int i , j , *rn = r+n , *san = sa+n , ta = 0 ,tb = (n+1)/3 , tbc = 0 , p;
r[n] = r[n+1] = 0;
for(i=0; i<n; i++) {
if(i%3!=0)
wa[tbc++]=i; //tbc表示起始位置模3为1或2的后缀个数
}
sort(r+2,wa,wb,tbc,m);
sort(r+1,wb,wa,tbc,m);
sort(r,wa,wb,tbc,m);
for(p=1,rn[F(wb[0])]=0,i=1; i<tbc; i++)
rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
if(p<tbc)
dc3(rn,san,tbc,p);
else {
for(i=0; i<tbc; i++)
san[rn[i]]=i;
}
//对所有起始位置模3等于0的后缀排序
for(i=0; i<tbc; i++) {
if(san[i]<tb)
wb[ta++]=san[i]*3;
}
if(n%3==1) //n%3==1,要特殊处理suffix(n-1)
wb[ta++]=n-1;
sort(r,wb,wa,ta,m);
for(i=0; i<tbc; i++)
wv[wb[i] = G(san[i])]=i;
//合并所有后缀的排序结果,保存在sa数组中
for(i=0,j=0,p=0; i<ta&&j<tbc; p++)
sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
for(; i<ta; p++)
sa[p]=wa[i++];
for(; j<tbc; p++)
sa[p]=wb[j++];
return;
} //height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀
void calheight(int *r,int *sa,int n) {
int i,j,k=0;
for(i=1; i<=n; i++)
rank1[sa[i]]=i;
for(i=0; i<n; height[rank1[i++]]=k)
for(k?k--:0,j=sa[rank1[i]-1]; r[i+k]==r[j+k]; k++);
} bool judge(int mid,int n,int k) {
int cnt = 1;
for(int i=1; i<=n; i++) {
if(height[i] >= mid) {
cnt ++;
} else cnt = 1;
if(cnt >= k) return true;
}
return false;
}
int xx[N],x[N];
int search(int v,int m) {
int l = 0,r = m-1;
while(l <= r) {
int mid = (l + r) /2;
if(x[mid] == v)
return mid;
if(v < x[mid])
r = mid-1;
else
l = mid+1;
}
return -1;
}
int main() {
int n,k;
cin >> n >> k;
for(int i=0; i<n; i++) {
scanf("%d",&x[i]);
xx[i] = x[i];
}
int m = 1;
for (int i=1; i<n; i++) { //离散化去重
if (x[i] != x[i-1]) x[m ++] = x[i];
}
sort(x,x+m);
for(int i=0; i<n; i++) r[i] = search(xx[i],m) + 1;
// for(int i=0; i<n; i++) cout << r[i] << ' ';
// cout << endl;
r[n] = 0; //要保证结尾最小
dc3(r,sa,n+1,20001);
calheight(r,sa,n);
int l=1, r=n,mid; //枚举长度
int ans = 0;
while(l <= r) {
mid = (l+r) >> 1;
if(judge(mid,n,k)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans << endl;
return 0;
}

POJ 3261 Milk Patterns(后缀数组+二分答案+离散化)的更多相关文章

  1. Poj 3261 Milk Patterns(后缀数组+二分答案)

    Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...

  2. poj 3261 Milk Patterns 后缀数组 + 二分

    题目链接 题目描述 给定一个字符串,求至少出现 \(k\) 次的最长重复子串,这 \(k\) 个子串可以重叠. 思路 二分 子串长度,据其将 \(h\) 数组 分组,判断是否存在一组其大小 \(\ge ...

  3. POJ 3261 Milk Patterns 后缀数组求 一个串种 最长可重复子串重复至少k次

    Milk Patterns   Description Farmer John has noticed that the quality of milk given by his cows varie ...

  4. POJ 3261 Milk Patterns(后缀数组+单调队列)

    题意 找出出现k次的可重叠的最长子串的长度 题解 用后缀数组. 然后求出heigth数组. 跑单调队列就行了.找出每k个数中最小的数的最大值.就是个滑动窗口啊 (不知道为什么有人写二分,其实写啥都差不 ...

  5. POJ 3261 Milk Patterns ( 后缀数组 && 出现k次最长可重叠子串长度 )

    题意 : 给出一个长度为 N 的序列,再给出一个 K 要求求出出现了至少 K 次的最长可重叠子串的长度 分析 : 后缀数组套路题,思路是二分长度再对于每一个长度进行判断,判断过程就是对于 Height ...

  6. Poj 1743 Musical Theme(后缀数组+二分答案)

    Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 28435 Accepted: 9604 Descri ...

  7. BZOJ 1717 [USACO06DEC] Milk Patterns (后缀数组+二分)

    题目大意:求可重叠的相同子串数量至少是K的子串最长长度 洛谷传送门 依然是后缀数组+二分,先用后缀数组处理出height 每次二分出一个长度x,然后去验证,在排序的后缀串集合里,有没有连续数量多于K个 ...

  8. POJ 1226 Substrings(后缀数组+二分答案)

    [题目链接] http://poj.org/problem?id=1226 [题目大意] 求在每个给出字符串中出现的最长子串的长度,字符串在出现的时候可以是倒置的. [题解] 我们将每个字符串倒置,用 ...

  9. poj 3294 Life Forms - 后缀数组 - 二分答案

    题目传送门 传送门I 传送门II 题目大意 给定$n$个串,询问所有出现在严格大于$\frac{n}{2}$个串的最长串.不存在输出'?' 用奇怪的字符把它们连接起来.然后求sa,hei,二分答案,按 ...

随机推荐

  1. [转载]Android 知识图谱

    from: http://blog.csdn.net/xyz_lmn/article/details/41411355

  2. BZOJ 3402: [Usaco2009 Open]Hide and Seek 捉迷藏

    题目 3402: [Usaco2009 Open]Hide and Seek 捉迷藏 Time Limit: 3 Sec  Memory Limit: 128 MB Description     贝 ...

  3. PHP程序猿必须学习的第二课——站点安全问题预防

    作为PHP程序猿.第一课我们学习了基本的语法.那么在熟悉基本的语法之后我们应该学些什么呢?我觉得是安全问题.安全问题基于一个站点宛如基石,一着不慎,意味着灾难性的事故. 这里主要就提三点最简单,也是最 ...

  4. Webstorm入门-----常用快捷键

    为了提高敲代码的速度.我们所需要关注的各种快捷键: 首先,快捷键的设置                  相关连接: http://www.cnblogs.com/dc10101/archive/20 ...

  5. Request url 各种属性值

    網址:http://localhost:1897/News/Press/Content.aspx/123?id=1#toc Request.ApplicationPath / Request.Phys ...

  6. 改变status bar的状态

    两种改变status bar状态的方法 一 :(全局的) 直接在当前控制器中(一般是在navigationcontroller) //- (UIStatusBarStyle)preferredStat ...

  7. 杭电ACM求平均成绩

    求平均成绩 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  8. 设计模式值六大原则——接口隔离原则 (ISP)

    接口隔离原则 Interface Segregation Principle    定义: 客户端不应该依赖它不需要的接口 类间的依赖关系应该建立在最小的接口上 我们可以把这两个定义概括为一句话:建立 ...

  9. 我的Python成长之路---第五天---Python基础(17)---2016年1月30日(晴)

    常用模块 1.模块介绍 模块,用一砣代码实现了某个功能的代码集合. 模块分为三种 自定义模块 内置标准模块(又称标准库) 开源模块 2.常用模块 time模块 time.time() import t ...

  10. 深入浅出—JAVA(2)

    2.类与对象 当你在设计类时,要记得对象是靠类的模型塑造出来的. 1.对象是已知的事物2.对象会执行的动作 对象本身已知的事物被称为 实例变量:它们代表对象的状态.切该类型的每一个对象都会独立的拥有一 ...