题意:给定一个字符串,求至少出现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. 关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)

    最近由于项目的需求,一直在研究Qt.信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信.这个参数会分别存在于信号的参数列表和槽函数的参数列表中.需要注意的是,若将槽函数绑定 ...

  2. 一劳永逸让windows 64位操作系统 禁止强制驱动签名

    如何让WINDOWS7 64位直接加载“禁用强制驱动程序签名”方式启动  Windows Client 论坛 > Windows 7 问题 0 登录进行投票 因为开发需要,要装一台设备的驱动,但 ...

  3. 深度学习 Deep Learning UFLDL 最新 Tutorial 学习笔记 1:Linear Regression

    1 前言 Andrew Ng的UFLDL在2014年9月底更新了. 对于開始研究Deep Learning的童鞋们来说这真的是极大的好消息! 新的Tutorial相比旧的Tutorial添加了Conv ...

  4. 高性能网站优化-创建快速响应的Web

    <高性能网站建设进阶指南> 优化原则 优化的目的是希望降低程序的整体开销. 减少开销 通常认为开销就是程序的执行时间.而在进行优化工作时,应该把重点放在对程序开销影响最大的那部分. 假设我 ...

  5. javascript 横向下拉菜单演示

    <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN"><head><me ...

  6. C# 常用参数

    主函数调用 public static void Fun_Param() { ; ; ChangeValue(x, y); //外部调用Ref函数,必须初始化变量 ChangeValue(ref x, ...

  7. Laravel OAuth2 (一) ---简单获取用户信息

    前言 本来要求是使用微信进行第三方登陆,所以想着先用 github 测试成功再用微信测试,可是最近拖了好久都还没申请好微信开放平台的 AppID ,所以就只写 github 的第三方登陆吧,估计微信的 ...

  8. QT学习 之 事件与事件过滤器(分为五个层次)

    事件 在Qt中,事件是作为对象处理的,所有事件对象继承自抽象类QEvent.此类用来表示程序内部发生或者来自于外部但应用程序应该知道的动作.事件能够能过被 QObject 的子类接受或者处理,但是通常 ...

  9. python 爬一下

    1.安装Requestswindow:pip install requestslinux:sudo pip install requests国内安装缓慢,建议到:http://www.lfd.uci. ...

  10. Linux学习:find、chmod、ps命令

    下面介绍下linux下find.chmod.ps这三个常见命令的使用. 这每个命令都有很多可选的参数,不同参数体现的功能不一样.我们这里不一一介绍各种参数的含义,只介绍最常见的使用场景. 一.find ...