题意:找一个出现了m次的最长子串,以及这时的最右的位置。

  hash的话代码还是比较好写的,,但是时间比SA多很多。。

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = + ;
typedef long long ll;
const int X = ;
const int mod = (int)1e9 + ; char s[N];
int m,len,pw[N];
int H[N],pos; struct node
{
int id,hash;
bool operator < (const node & temp) const
{
return hash == temp.hash ? id < temp.id : hash < temp.hash;
}
}p[N]; bool solve(int L)
{
int cnt = ;
pos = -;
for(int i=;i+L-<=len;i++)
{
int id = i;
int hash = ((ll)H[i] - (ll)H[i+L]*pw[L]) % mod;
if(hash < ) hash += mod; // 注意这里!
p[i] = (node){id, hash};
}
sort(p+, p++ len - L + ); for(int i=;i<=len-L+;i++)
{
if(i == || p[i].hash != p[i-].hash) cnt = ;
if(++cnt >= m) pos = max(pos, p[i].id);
}
return pos != -;
} int main()
{
pw[] = ;
for(int i=;i<N;i++) pw[i] = 1LL*pw[i-] * X % mod;
while(scanf("%d",&m) == && m)
{
scanf("%s",s+);
len = strlen(s+);
for(int i=len;i>=;i--) H[i] = (1LL*H[i+] * X + s[i] - 'a') % mod;
int l = , r = len, ans = -;
while(l <= r)
{
int mid = l + r >> ;
if(solve(mid)) ans = mid, l = mid + ;
else r = mid - ;
}
solve(ans);
if(ans != -) printf("%d %d\n",ans, pos-);
else puts("none");
}
return ;
}

hash的写法

  

  SA的话,写法需要细细体会一下了。。时间减少了很多。

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = + ;
typedef long long ll; /**
* sa[i]:表示排在第i位的后缀的起始下标
* rank[i]:表示后缀suffix(i)排在第几
* height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
*
* */
/*
如果整数的话模板改成int.
加一个数a[n] = 0 。 这样他的排名是第一个。
construct(a,n+1); 字符串的话。
len = strlen(str);
construct(s,strlen(s)+1);
排名第0的是个空字符串。 height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
所以height[1] = 0;
rank[len] = 0;
sa[0] = len;
*/
int sa[N],rnk[N],height[N];
void construct(const char *s,int n,int m = ) {
static int t1[N],t2[N],c[N];
int *x = t1,*y = t2;
int i,j,k,p,l;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[i] = s[i]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[i]]] = i;
for (k = ; k <= n; k <<= ) {
p = ;
for (i = n - k; i < n; ++ i) y[p++] = i;
for (i = ; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[y[i]]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[y[i]]]] = y[i];
std::swap(x,y);
p = ; x[sa[]] = ;
for (i = ; i < n; ++ i)
x[sa[i]] = y[sa[i - ]] == y[sa[i]]
&& y[sa[i - ] + k] == y[sa[i] + k] ? p - : p ++;
if (p >= n) break;
m = p;
}
for (i = ; i < n; ++ i) rnk[sa[i]] = i;
for (i = ,l = ; i < n; ++ i) {
if (rnk[i]) {
j = sa[rnk[i] - ];
while (s[i + l] == s[j + l]) l++;
height[rnk[i]] = l;
if (l) l--;
}
}
} int m,pos,len;
char s[N];
bool solve(int L)
{
// 用后缀数组的话,如果重复长度就是最长的长度,需要特判
if(m == && L == len) {pos = ; return ;}
pos = -;
int cnt = ;
int temp = -; // 要用一个temp来记录当前cnt>=m的最大的位置
for(int i=;i<=len;i++)
{
if(height[i] >= L) cnt++, temp = max(temp, sa[i]);
else cnt = , temp = sa[i];
if(cnt >= m) pos = max(pos, temp);
}
return pos != -;
} int main()
{
while(scanf("%d",&m) == && m)
{
scanf("%s",s);
len = strlen(s);
construct(s, len+);
int l = , r = len;
int ans = -;
while(l <= r)
{
int mid = l + r >> ;
if(solve(mid)) ans = mid, l = mid + ;
else r = mid - ;
}
solve(ans);
if(ans != -) printf("%d %d\n",r,pos);
else puts("none");
}
return ;
}

SA的写法

  顺便想说一下的是,这里不知为何下标从1开始无限WA,以后用SA还是下标从0开始好了。。毕竟不懂SA的具体原理。

UVALive - 4513 Stammering Aliens ——(hash+二分 || 后缀数组加二分)的更多相关文章

  1. [bzoj1717][Usaco2006 Dec]Milk Patterns 产奶的模式 (hash构造后缀数组,二分答案)

    以后似乎终于不用去学后缀数组的倍增搞法||DC3等blablaSXBK的方法了= = 定义(来自关于后缀数组的那篇国家集训队论文..) 后缀数组:后缀数组SA是一个一维数组,它保存1..n的某个排列S ...

  2. uvalive 4513 Stammering Aliens

    题意:给你一个串,问期中至少出现m次的最长子串及其起始位置的坐标. 思路:hash+LCP+二分答案 #include<cstdio> #include<cstring> #i ...

  3. BZOJ 2946 [Poi2000]公共串 (二分+Hash/二分+后缀数组/后缀自动机)

    求多串的最长公共字串. 法1: 二分长度+hash 传送门 法2: 二分+后缀数组 传送门 法3: 后缀自动机 拿第一个串建自动机,然后用其他串在上面匹配.每次求出SAM上每个节点的最长匹配长度后,再 ...

  4. Hash(LCP) || 后缀数组 LA 4513 Stammering Aliens

    题目传送门 题意:训练指南P225 分析:二分寻找长度,用hash值来比较长度为L的字串是否相等. #include <bits/stdc++.h> using namespace std ...

  5. HDU4080 Stammering Aliens(二分 + 后缀数组)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4080 Description Dr. Ellie Arroway has establish ...

  6. FJUT3703 这还是一道数论题(二分 + hash + manacher 或者 STL + hash 或者 后缀数组 + hash)题解

    Problem Description 最后来个字符串签个到吧,这题其实并不难,所需的算法比较基础,甚至你们最近还上过课. 为了降低难度,免得所有人爆零.这里给几个提示的关键字 :字符串,回文,二分, ...

  7. 140. 后缀数组(hash + 二分 / 后缀数组)

    题目链接 : https://www.acwing.com/problem/content/description/142/ Hash + 二分 #include <bits/stdc++.h& ...

  8. poj 2774 最长公共子--弦hash或后缀数组或后缀自己主动机

    http://poj.org/problem?id=2774 我想看看这里的后缀数组:http://blog.csdn.net/u011026968/article/details/22801015 ...

  9. 后缀数组LCP + 二分 - UVa 11107 Life Forms

    Life Forms Problem's Link Mean: 给你n个串,让你找出出现次数大于n/2的最长公共子串.如果有多个,按字典序排列输出. analyse: 经典题. 直接二分判断答案. 判 ...

随机推荐

  1. myBatis之事务管理

    1. myBatis单独使用时,使用SqlSession来处理事务: public class MyBatisTxTest { private static SqlSessionFactory sql ...

  2. LINUX 磁盘如何分区

    fdisk -l  可以查看当前磁盘 假设未分配磁盘为/dev/sdb    size=10G fdisk /dev/sdb  (m for help) 按照提示应该可以分区成功,注意一点   一个磁 ...

  3. ubuntu下快速制作linux 系统安装盘

    1. 安装unetbootin sudo apt-get install unetbooin 2. 超级系统管理员启动,选择镜像文件并安装 先插入空白USB,然后打开终端输入命令 su - 输入密码, ...

  4. 常用SQL语句

    一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...

  5. 对话框AlertDialog.Builder使用方法

    我们在平时做开发的时候,免不了会用到各种各样的对话框,相信有过其他平台开发经验的朋友都会知道,大部分的平台都只提供了几个最简单的实现,如果我们想实现自己特定需求的对话框,大家可能首先会想到,通过继承等 ...

  6. oracle ||,

    || oracle数据库中的 ||称为 "字符串连接符" 用于连接查询结果,如下: select trade_id,accept_date from A; ------------ ...

  7. 微信小程序上传文件

    wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourc ...

  8. <页面里折合与打开>

    主要思想是:通过覆盖,显示的方式.visible 为 true与false,id以及function函数中参数的不同. 具体代码如下: <script type="text/javas ...

  9. SQL Server 数据库备份

    declare @filename varchar(1024) declare @SQLDB varchar(50) declare @path varchar(1024) set @path = N ...

  10. MVC中的Html.Partial和Html.RenderPartial

    Partial辅助方法用于将部分视图渲染成字符串.注意没必要为视图指定路径和文件扩展名,因为运行时定位部分视图与定位正常视图使用的逻辑相同.例如,下面代码就渲染一个名为AlbumDisplay的部分视 ...