写一发后缀数组套路题,看起来简单,写起来要人命哦~~~

总共13题。

分两天debug吧,有点累了~~~

suffix(后缀数组的应用)

  • sa[i] :排名第 i 的后缀在哪(i 从 1 开始)

  • rank[i]:后缀 i 排第几 (i 从 0 开始)

  • height[i]:排名为 i 和 i-1 的两个后缀的最长公共前缀(LCP)长度 (i 从 2 开始)

模板:加上RMQ操作

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
​
​
using namespace std;
​
const int maxn = +;
​
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int sa[maxn];
int r[maxn];
​
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=; i<m; i++) ws[i]=;
for(i=; i<n; i++) ws[x[i]=r[i]]++;
for(i=; i<m; i++) ws[i]+=ws[i-];
for(i=n-; i>=; i--) sa[--ws[x[i]]]=i;
for(j=,p=; p<n; j*=,m=p)
{
for(p=,i=n-j; i<n; i++) y[p++]=i;
for(i=; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=; i<n; i++) wv[i]=x[y[i]];
for(i=; i<m; i++) ws[i]=;
for(i=; i<n; i++) ws[wv[i]]++;
for(i=; i<m; i++) ws[i]+=ws[i-];
for(i=n-; i>=; i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=,x[sa[]]=,i=; i<n; i++)
x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
}
return;
}
int ranks[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
int i,j,k=;
for(i=; i<=n; i++) ranks[sa[i]]=i;
for(i=; i<n; height[ranks[i++]]=k)
for(k?k--:,j=sa[ranks[i]-]; r[i+k]==r[j+k]; k++);
return;
}
​
​
char str[maxn];
​
int f[maxn][];
void init(int len) {
for(int i = ; i <= len; i++) f[i][] = height[i];
for(int s = ; (<<s)<=len; s++) {
int tmp = (<<s);
for(int i = ; i+tmp-<=len; i++) {
f[i][s] = min(f[i][s-],f[i+tmp/][s-]);
}
}
}
​
int cal(int l,int r) {
int len = log2(r-l+);
int ans = min(f[l][len],f[r-(<<len)+][len]);
return ans;
}
​
​
​
int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
int n = strlen(str);
​
for(int i = ; i < n; i++)
r[i] = str[i] - 'a' + ;
da(r,sa,n+,);
calheight(r,sa,n);
​
for(int i = ; i <= n; i++) {
printf("%d ",sa[i]);
}
puts("");
​
for(int i = ; i < n; i++) {
printf("%d ",ranks[i]);
}
puts("");
​
for(int i = ; i <= n; i++) {
printf("%d ",height[i]);
}
puts("");
​
init(n);
​
//height 上的 RMQ(); 从 2开始;
printf("%d\n",cal(,));
return ;
}

例题一:最长公共前缀

给定一个字符串,询问某两个后缀的最长公共前缀。

分析:根据图,某两个后缀的LCP,是一个区间的RMQ;(也是定理)


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
​
​
using namespace std;
​
const int maxn = +;
​
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int sa[maxn];
int r[maxn];
​
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=; i<m; i++) ws[i]=;
for(i=; i<n; i++) ws[x[i]=r[i]]++;
for(i=; i<m; i++) ws[i]+=ws[i-];
for(i=n-; i>=; i--) sa[--ws[x[i]]]=i;
for(j=,p=; p<n; j*=,m=p)
{
for(p=,i=n-j; i<n; i++) y[p++]=i;
for(i=; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=; i<n; i++) wv[i]=x[y[i]];
for(i=; i<m; i++) ws[i]=;
for(i=; i<n; i++) ws[wv[i]]++;
for(i=; i<m; i++) ws[i]+=ws[i-];
for(i=n-; i>=; i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=,x[sa[]]=,i=; i<n; i++)
x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
}
return;
}
int ranks[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
int i,j,k=;
for(i=; i<=n; i++) ranks[sa[i]]=i;
for(i=; i<n; height[ranks[i++]]=k)
for(k?k--:,j=sa[ranks[i]-]; r[i+k]==r[j+k]; k++);
return;
}
​
​
char str[maxn];
​
int f[maxn][];
void init(int len) {
for(int i = ; i <= len; i++) f[i][] = height[i];
for(int s = ; (<<s)<=len; s++) {
int tmp = (<<s);
for(int i = ; i+tmp-<=len; i++) {
f[i][s] = min(f[i][s-],f[i+tmp/][s-]);
}
}
}
​
int cal(int l,int r) {
int len = log2(r-l+);
int ans = min(f[l][len],f[r-(<<len)+][len]);
return ans;
}
​
​
​
int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
int n = strlen(str);
​
for(int i = ; i < n; i++)
r[i] = str[i] - 'a' + ;
da(r,sa,n+,);
calheight(r,sa,n);
​
for(int i = ; i <= n; i++) {
printf("%d ",sa[i]);
}
puts("");
​
for(int i = ; i < n; i++) {
printf("%d ",ranks[i]);
}
puts("");
​
for(int i = ; i <= n; i++) {
printf("%d ",height[i]);
}
puts("");
​
init(n);
​
int l,r; // 询问后缀L,R的最长公共前缀,从0开始
​
scanf("%d%d",&l,&r);
l = ranks[l];
r = ranks[r];
​
if(l>r) swap(l,r);
​
l++;
printf("%d\n",cal(l,r));
​
return ;
}

例题二:可以重叠的最长重复子串

给定一个字符串,求最长的重复子串(出现了多次>1),这两个子串可以重叠。

分析:子串可以写成一个后缀,题目可以转换为求:最长的两个后缀的最长公共前缀。任意两个后缀的LCP,都是height数组里面某一段的最小值,那么这个值一定不大于height 的最大值。

int ans = -;
for(int i = ; i <= n; i++)
ans = max(ans,height[i]);

例题三:不可重叠最长重复子串(pku1743)

给定一个字符串,求最长重复子串,这两个子串不能重叠。(当然原题题意没这么简单咯~~~原题是音乐家演奏,求两组最长的旋律,要求这两组旋律一下(只可以相差一个常数))

我这里写字符串的,其实转换过来很简单的(考虑其差值,就转换过来了)

分析:遇到不重叠——二分分组。

先二分答案,把问题变成一个判定性题目,将排序排序后的后缀按照mid 分成若干组,每组的后缀自检的height>=mid,每个区间内找两个后缀满足不重叠即可。

原理:可以看出,游戏王成为最长公共前缀>=mid 的两个后缀,一定在一个分组里面。这个分组里面查不相交的两个位置。


int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
int n = strlen(str);
​
for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;
​
da(r,sa,n+,);
calheight(r,sa,n);
​
int l = ,r= n;
int ans = ;
while(l<=r) {
int mid = l + (r-l)/;
int L = inf,R= -inf;
bool flag = false;
for(int i = ; i <= n; i++) {
if(height[i]>=mid) { //按照 mid 分组
L = min(L,sa[i]);
L = min(L,sa[i-]);
R = max(R,sa[i]);
R = max(R,sa[i-]);
}
else
{
if(L+mid+<=R) {
flag = true;
ans = mid;
}
L = inf;
R = -inf;
}
}
​
if(L+mid+<=R) flag = true;
if(flag) l = mid+;
else r = mid - ;
}
​
printf("%d\n",ans);
​
return ;
}

例题四:可重叠k次最长重复子串(pku3261)

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

分析:此题可以重叠,二分答案,将后缀分组,由例题三和例题二,可以看出,只要判断是否存在一个分组使得其中元素>=k(重复k次嘛~~~)


int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
int n = strlen(str);
int k;
scanf("%d",&k);
​
for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;
​
da(r,sa,n+,);
calheight(r,sa,n);
​
for(int i = ; i<= n;i++)
printf("%d ",height[i]);
puts("");
​
int l = ,r= n;
int ans = ;
while(l<=r) {
int mid = l + (r-l)/;
int L = inf,R= -inf;
bool flag = false;
int cnt = ;
for(int i = ; i <= n; i++) {
if(height[i]>=mid) { //按照 mid 分组
cnt++;
}
else
{
if(cnt>=k) {
flag = true;
ans = mid;
}
L = inf;
R = -inf;
cnt = ;
}
}
if(flag) l = mid+;
else r = mid - ;
}
​
printf("%d\n",ans);
​
return ;
}

例题五:子串的个数(spoj694,spoj705)

给定一个字符串,求不相同的子串个数。

这个题今年多校考过~~~~不过当时GG了。

每一个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。

每个后缀产生n-sa[i] 个前缀,其中有height[i] 已经与前面重复~~~


int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
int n = strlen(str);
​
for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;
​
da(r,sa,n+,);
calheight(r,sa,n);
​
for(int i = ; i<= n;i++)
printf("%d ",height[i]);
puts("");
​
int cnt = n - sa[];
​
for(int i = ; i <= n; i++)
cnt = cnt + n - sa[i] - height[i];
printf("%d\n",cnt);
​
return ;
}

例题六:最长回文子串(ural11297)

给定一个字符串,求最长回文子串。

先搞一发Manacher;

#include <bits/stdc++.h>
using namespace std;
​
const int maxn = ;
​
char instr[maxn],str[maxn*];
int rad[maxn*];
​
​
int Manacher()
{
int i,j,maxx;
int n = strlen(instr);
memset(str,'#',sizeof(str));
for(i=;i<n;i++)
str[(i+)<<] = instr[i];
​
n = (n+)<<;
str[n] = '$';
int maxRad;
maxRad = j = maxx = ;
for(i = ;i<n;i++)
{
if(i<maxx)
rad[i] = min(rad[*j-i],maxx-i);
else rad[i] = ;
​
while(str[i-rad[i]]==str[i+rad[i]])
rad[i] ++;
if(maxRad<rad[i])
maxRad = rad[i];
if(rad[i]+i>maxx)
{
j = i;
maxx = rad[i] + i;
}
​
}
return maxRad;
​
}
​
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",instr);
printf("%d\n",Manacher()-);
}
return ;
}

但是,后缀数组的思路是非常重要的,重要到关于两个字符串的题目,extend_kmp很难的话,那么基本上就是后缀数组了~~~

分析:回文——将字符串倒着加到后面,枚举回文的中心点,而只要考虑回文的一侧,另一侧则在对称面,这两个后缀求LCP~~~

int main()
{
freopen("in.txt","r",stdin);
​
scanf("%s",str);
​
int n = strlen(str);
int tmpn = n;
str[n] = '$';
​
for(int i = n+,j=; i <= *n; i++,j++)
str[i] = str[n--j];
​
puts(str);
​
for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;
r[n] = ;
for(int i = n+; i < *n+; i++) r[i] = str[i] - 'a' + ;
n = strlen(str);
//
// da(r,sa,n+1,300);
// calheight(r,sa,n);
//
// for(int i =2 ; i<= n;i++)
// printf("%d ",height[i]);
// puts("");
​
​
da(r,sa,n,);
calheight(r,sa,n);
​
for(int i = ; i <= n; i++)
printf("%d ",sa[i]);
puts("");
​
init(n);
​
int ans = ;
for(int i = ; i < tmpn; i++) { //枚举中心
int l = i;
int r = n - i - ;
l = ranks[l];
r = ranks[r];
​
l++;
​
int lcp = cal(l,r); //回文是奇数
if(lcp*->ans) {
ans = lcp * -;
}
​
l = i;
if(l!=tmpn) {
l++;
r = n - l - ;
l = ranks[l];
r = ranks[r];
l++;
lcp = cal(l,r);
ans = max(ans,lcp*);
}
​
​
}
printf("%d\n",ans);
​
​
return ;
}

ACM-ICPC(10/21)的更多相关文章

  1. hduoj 4712 Hamming Distance 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4712 Hamming Distance Time Limit: 6000/3000 MS (Java/Other ...

  2. 2015 ACM / ICPC 亚洲区域赛总结(长春站&北京站)

    队名:Unlimited Code Works(无尽编码)  队员:Wu.Wang.Zhou 先说一下队伍:Wu是大三学长:Wang高中noip省一:我最渣,去年来大学开始学的a+b,参加今年区域赛之 ...

  3. ACM/ICPC 之 BFS(离线)+康拓展开(TSH OJ-玩具(Toy))

    祝大家新年快乐,相信在新的一年里一定有我们自己的梦! 这是一个简化的魔板问题,只需输出步骤即可. 玩具(Toy) 描述 ZC神最擅长逻辑推理,一日,他给大家讲述起自己儿时的数字玩具. 该玩具酷似魔方, ...

  4. ACM ICPC 2015 Moscow Subregional Russia, Moscow, Dolgoprudny, October, 18, 2015 D. Delay Time

    Problem D. Delay Time Input file: standard input Output file: standard output Time limit: 1 second M ...

  5. 【转】lonekight@xmu·ACM/ICPC 回忆录

    转自:http://hi.baidu.com/ordeder/item/2a342a7fe7cb9e336dc37c89 2009年09月06日 星期日 21:55 初识ACM最早听说ACM/ICPC ...

  6. hduoj 4715 Difference Between Primes 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Time Limit: 2000/1000 MS (J ...

  7. hduoj 4707 Pet 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4707 Pet Time Limit: 4000/2000 MS (Java/Others)    Memory ...

  8. hduoj 4706 Children&#39;s Day 2013 ACM/ICPC Asia Regional Online —— Warmup

    http://acm.hdu.edu.cn/showproblem.php?pid=4706 Children's Day Time Limit: 2000/1000 MS (Java/Others) ...

  9. 2016 ACM/ICPC Asia Regional Qingdao Online 1001/HDU5878 打表二分

    I Count Two Three Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  10. 2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp

    QSC and Master Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

随机推荐

  1. rancher1.X+docker+k8s搭建容器管理集群

    一, 环境准备 服务器 Linux k8s-m -.el7.x86_64 #1 SMP Fri Apr 20 16:44:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Li ...

  2. Thinkphp基于规则的Auth权限认证类

      PS:onethink是基于该权限认证类实现,Auth类作为官方类库,在Library\Think里面. 其实Auth类也是基于角色访问控制RBAC扩展的,具体到节点的权限校验方式还是需要根据业务 ...

  3. C#异步执行带有返回值和参数的方法,且获取返回值

    很多时候需要用到这些小知识点,做做笔记一起成长 下面是需要异步执行的方法 //获取所有的邮件 private List<EmailModel> GetEmailOnlyCount(POP3 ...

  4. Red–black tree ---reference wiki

    source address:http://en.wikipedia.org/wiki/Red%E2%80%93black_tree A red–black tree is a type of sel ...

  5. 004 java中的变量

    这篇文章为你搞懂三个问题 什么是变量? 如何使用变量? 变量命名有哪些规范? 变量 计算机的内存类似于人的大脑,电脑使用内存来存储计算所需要的数据. 内存像旅馆一样,不同的房间类型对应不同的数据类型, ...

  6. BNU27932——Triangle——————【数学计算面积】

    Triangle Time Limit: 1000ms Memory Limit: 65536KB 64-bit integer IO format: %lld      Java class nam ...

  7. bzoj 4771: 七彩树

    Description 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有 ...

  8. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解

    [2013-12-06 11:06:21,715] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|7 ...

  9. 9th week -the history of program 1950~2020

    We already know that programming language is a formal language designed to communicate instructions ...

  10. 【数据库】9.0 MySQL入门学习(九)——获得数据库和表的信息、日期计算、查询、选择特殊列

    1.0 SELECT语句用来从数据表中检索信息. SELECT what_to_select FROM which_table WHERE conditions_to_satisfy; what_to ...