后缀数组(SA)学习记录
一只只会后缀自动机却不会后缀数组的弱鸡做了一下HDU - 1403,结果SAM被卡内存了,然后学习了一下SA。
以下两道题都是求LCS,区别在于字符串长度。
参考blog:https://www.cnblogs.com/victorique/p/8480093.html
HDU - 1403
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rank Rank
using namespace std;
const int MAXN = 2e5+;
char str[MAXN];
int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN];
//rank[i] 第i个后缀的排名, SA[i] 排名为i的后缀的位置, Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//sum[i] 基数排序辅助数组, 存储小于i的元素有多少个, tp[i] rank的辅助数组(按第二关键字排序的结果),与SA意义一样
bool cmp(int *f, int x, int y, int w){return f[x] == f[y] && f[x + w] == f[y + w];} void get_SA(char *s, int n, int m)
{
for(int i = ; i < m; i++) sum[i] = ;
for(int i = ; i < n; i++) sum[rank[i] = s[i]]++;
for(int i = ; i < m; i++) sum[i] += sum[i - ];
for(int i = n - ; i >= ; i--) SA[--sum[rank[i]]] = i;
for(int len = ; len <= n; len <<= )
{
int p = ;
for(int i = n - len; i < n; i++) tp[p++] = i;
for(int i = ; i < n; i++)
if(SA[i] >= len)
tp[p++] = SA[i] - len;
for(int i = ; i < m; i++) sum[i] = ;
for(int i = ; i < n; i++) sum[rank[tp[i]]]++;
for(int i = ; i < m; i++) sum[i] += sum[i - ];
for(int i = n - ; i >= ; i--) SA[--sum[rank[tp[i]]]] = tp[i];
swap(rank, tp);
p = ;
rank[SA[]] = ;
for(int i = ; i < n; i++)
rank[SA[i]] = cmp(tp, SA[i - ], SA[i], len) ? p - : p++;
if(p >= n) break;
m = p;
}
int k = ;
n--;
for(int i = ; i <= n; i++) rank[SA[i]] = i;
for(int i = ; i < n; i++)
{
if(k) k--;
int j = SA[rank[i] - ];
while(s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
int main()
{
while(~scanf("%s", str))
{
int len = strlen(str);
str[len] = '';
scanf("%s", str + len + );
int n = strlen(str);
str[n] = ; //末尾添加一个0
get_SA(str, n + , 'z' + );
int sol = ;
for(int i = ; i < n; i++)
{
if(SA[i] > len && SA[i - ] < len) sol = max(sol, height[i]);
if(SA[i] < len && SA[i - ] > len) sol = max(sol, height[i]);
}
printf("%d\n", sol);
}
return ;
}
SPOJ - LCS
SA版本:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rank Rank
using namespace std;
const int MAXN = 5e5+;
char str[MAXN];
int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN];
//rank[i] 第i个后缀的排名, SA[i] 排名为i的后缀的位置, Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//sum[i] 基数排序辅助数组, 存储小于i的元素有多少个, tp[i] rank的辅助数组(按第二关键字排序的结果),与SA意义一样
bool cmp(int *f, int x, int y, int w){return f[x] == f[y] && f[x + w] == f[y + w];} void get_SA(char *s, int n, int m)
{
for(int i = ; i < m; i++) sum[i] = ;
for(int i = ; i < n; i++) sum[rank[i] = s[i]]++;
for(int i = ; i < m; i++) sum[i] += sum[i - ];
for(int i = n - ; i >= ; i--) SA[--sum[rank[i]]] = i;
for(int len = ; len <= n; len <<= )
{
int p = ;
for(int i = n - len; i < n; i++) tp[p++] = i;
for(int i = ; i < n; i++)
if(SA[i] >= len)
tp[p++] = SA[i] - len;
for(int i = ; i < m; i++) sum[i] = ;
for(int i = ; i < n; i++) sum[rank[tp[i]]]++;
for(int i = ; i < m; i++) sum[i] += sum[i - ];
for(int i = n - ; i >= ; i--) SA[--sum[rank[tp[i]]]] = tp[i];
swap(rank, tp);
p = ;
rank[SA[]] = ;
for(int i = ; i < n; i++)
rank[SA[i]] = cmp(tp, SA[i - ], SA[i], len) ? p - : p++;
if(p >= n) break;
m = p;
}
int k = ;
n--;
for(int i = ; i <= n; i++) rank[SA[i]] = i;
for(int i = ; i < n; i++)
{
if(k) k--;
int j = SA[rank[i] - ];
while(s[i + k] == s[j + k]) k++;
height[rank[i]] = k;
}
}
int main()
{
while(~scanf("%s", str))
{
int len = strlen(str);
str[len] = '';
scanf("%s", str + len + );
int n = strlen(str);
str[n] = ; //末尾添加一个0
get_SA(str, n + , 'z' + );
int sol = ;
for(int i = ; i < n; i++)
{
if(SA[i] > len && SA[i - ] < len) sol = max(sol, height[i]);
if(SA[i] < len && SA[i - ] > len) sol = max(sol, height[i]);
}
printf("%d\n", sol);
}
return ;
}
SAM版本:
#include<bits/stdc++.h>
using namespace std;
const int kind=;
const int maxn=;
struct state
{
state *Next[kind],*link;
int len;
state()
{
link=;
len=;
memset(Next,,sizeof(Next));
}
};
int sz;
state st[maxn*+];
inline state* newnode(int len = )
{
memset(st[sz].Next,,sizeof(st[sz].Next));
st[sz].link=;
st[sz].len=len;
return &st[sz++];
}
state *root,*last;
void extend(int w)
{
state* p=last;
state* cur=newnode(p->len+);
while(p&&p->Next[w]==)
{
p->Next[w]=cur;
p=p->link;
}
if(p)
{
state* q=p->Next[w];
if(p->len+==q->len)
cur->link=q;
else
{
state* clone=newnode(p->len+);
memcpy(clone->Next,q->Next,sizeof(q->Next));
clone->link=q->link;
q->link=clone;
cur->link=clone;
while(p&&p->Next[w]==q)
{
p->Next[w]=clone;
p=p->link;
}
}
}
else cur->link=root;
last=cur;
}
string keyword;
int main()
{
ios::sync_with_stdio(false);
while(cin>>keyword)
{
sz=;
int ans=;
root=newnode();
last=root;
for(int i=;i<keyword.size();i++)
extend(keyword[i]-'a');
cin>>keyword;
state *p=root;
int tmp=;
for(int i=;i<keyword.size();i++)
{
if(p->Next[keyword[i]-'a'])
{
tmp++;
p=p->Next[keyword[i]-'a'];
}
else
{ while(p&&!p->Next[keyword[i]-'a'])
p=p->link;
if(!p)
p=root;
if(p->Next[keyword[i]-'a'])
{
tmp=p->len+;
p=p->Next[keyword[i]-'a'];
}
else
tmp=;
}
ans=max(ans,tmp);
}
cout<<ans<<endl;
}
return ;
}
后缀数组(SA)学习记录的更多相关文章
- 后缀数组SA学习笔记
什么是后缀数组 后缀数组\(sa[i]\)表示字符串中字典序排名为\(i\)的后缀位置 \(rk[i]\)表示字符串中第\(i\)个后缀的字典序排名 举个例子: ababa a b a b a rk: ...
- 后缀数组SA入门(史上最晦涩难懂的讲解)
参考资料:victorique的博客(有一点锅无伤大雅,记得看评论区),$wzz$ 课件(快去$ftp$%%%),$oi-wiki$以及某个人的帮助(万分感谢!) 首先还是要说一句:我不知道为什么我这 ...
- 后缀数组(SA)总结
后缀数组(SA)总结 这个东西鸽了好久了,今天补一下 概念 后缀数组\(SA\)是什么东西? 它是记录一个字符串每个后缀的字典序的数组 \(sa[i]\):表示排名为\(i\)的后缀是哪一个. \(r ...
- bzoj3796(后缀数组)(SA四连)
bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...
- [笔记]后缀数组SA
参考资料这次是真抄的: 1.后缀数组详解 2.后缀数组-学习笔记 3.后缀数组--处理字符串的有力工具 定义 \(SA\)排名为\(i\)的后缀的位置 \(rk\)位置为\(i\)的后缀的排名 \(t ...
- 浅谈后缀数组SA
这篇博客不打算讲多么详细,网上关于后缀数组的blog比我讲的好多了,这一篇博客我是为自己加深印象写的. 给你们分享了那么多,容我自私一回吧~ 参考资料:这位dalao的blog 一.关于求Suffix ...
- 【字符串】后缀数组SA
后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\),其中 \(sa[i]\) 表示排名为 i 的后缀,\(rk[i]\) 表示后 ...
- 洛谷.3809.[模板]后缀排序(后缀数组 倍增) & 学习笔记
题目链接 //输出ht见UOJ.35 #include<cstdio> #include<cstring> #include<algorithm> const in ...
- 后缀数组SA
复杂度:O(nlogn) 注:从0到n-1 const int maxn=1e5; char s[maxn]; int sa[maxn],Rank[maxn],height[maxn],rmq[max ...
- 洛谷2408不同字串个数/SPOJ 694/705 (后缀数组SA)
真是一个三倍经验好题啊. 我们来观察这个题目,首先如果直接整体计算,怕是不太好计算. 首先,我们可以将每个子串都看成一个后缀的的前缀.那我们就可以考虑一个一个后缀来计算了. 为了方便起见,我们选择按照 ...
随机推荐
- 使用Qt生成第一个窗口程序
一.打开QtCreater,点击New Project 二.在Qt中,最常用的窗口程序为widgets控件程序,这里我们选择Qt Widgets Application 三.Qt生成的debug和re ...
- ASP.NET MVC 长连接(服务器推)完整实现
1.什么是"服务器推"(百科来一波)? 传统模式的 Web 系统以客户端发出请求.服务器端响应的方式工作.这种方式并不能满足很多现实应用的需求,譬如: 监控系统:后台硬件热插拔.L ...
- IOS PickerView使用
- (void)viewDidLoad { [super viewDidLoad]; // 1.创建pickerview // pickerview有默认的frame UIPickerView *pi ...
- POJ 3057 Evacuation(二分匹配)
分析: 这是一个时间和门的二元组(t,d)和人p匹配的问题,当我们固定d0时,(t,d0)匹配的人数和t具有单调性. t增加看成是多增加了边就行了,所以bfs处理出p到每个d的最短时间,然后把(t,d ...
- 2017.12.11 String 类中常用的方法
1.编写程序将 "jdk" 全部变为大写,并输出到屏幕,截取子串"DK" 并输出到屏幕 package demo; import java.util.Scann ...
- 解决ssh登录慢,等待时间长的问题
有时候在ssh远程登录到其他主机上时发现登录时间太长,经过亲自测试,发现主要有两个问题会导致ssh登录慢: 1.使用了dns反查,这样的话当ssh某个IP时,系统会试图通过DNS反查相对应的域名,如果 ...
- C/C++程序基础 (八)数据结构
非递归先序遍历 // 输出, 遍历左子树,遍历右子树 void firstOrder(Node* root) { stack<Node*> leftNodes; Node* curr = ...
- js时间转换
1. 将时间戳转换成日期格式 // 简单的一句代码 var date = new Date(时间戳); //获取一个时间对象 /** 1. 下面是获取时间日期的方法,需要什么样的格式自己拼接起来就好了 ...
- 三十四、MySQL 函数
MySQL 函数 MySQL 有很多内置的函数,以下列出了这些函数的说明. MySQL 字符串函数 函数 描述 实例 ASCII(s) 返回字符串 s 的第一个字符的 ASCII 码. 返回 Cust ...
- linux关于权限
用户权限:drwxr-x---. 8 root root 4096 8月 6 23:18 mnt 第一个root:所有者 即root用户第二个root:所有者所在的组mnt:所有者创建的文件夹Rwx: ...