HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分
http://acm.hdu.edu.cn/showproblem.php?pid=5558
对于每个后缀suffix(i),想要在前面i - 1个suffix中找到一个pos,使得LCP最大。这样做O(n^2)
考虑到对于每一个suffix(i),最长的LCP肯定在和他排名相近的地方取得。
按排名大小顺序枚举位置,按位置维护一个递增的单调栈,对于每一个进栈的元素,要算一算栈内元素和他的LCP最大是多少。
如果不需要输出最小的下标,最大的直接是LCP(suffix(st[top]), suffix(st[top - 1]))就是相邻的两个。
但是要求最小的下标,这样的话需要对整个栈进行一个遍历,找到下标最小的并且长度最大的。整个栈与新进来的栈顶元素的LCP存在单调性,单调递增,所以可以二分。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#define X first
#define Y second
#define clr(u,v); memset(u,v,sizeof(u));
#define in() freopen("data.txt","r",stdin);
#define out() freopen("ans","w",stdout);
#define Clear(Q); while (!Q.empty()) Q.pop();
#define pb push_back
#define inf (0x3f3f3f3f)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = + ;
int sa[maxn], x[maxn], y[maxn], book[maxn];
bool cmp(int r[], int a, int b, int len) {
return r[a] == r[b] && r[a + len] == r[b + len];
}
void da(char str[], int sa[], int lenstr, int mx) {
int *fir = x, *sec = y, *ToChange;
for (int i = ; i <= mx; ++i) book[i] = ;
for (int i = ; i <= lenstr; ++i) {
fir[i] = str[i];
book[str[i]]++;
}
for (int i = ; i <= mx; ++i) book[i] += book[i - ];
for (int i = lenstr; i >= ; --i) sa[book[fir[i]]--] = i;
for (int j = , p = ; p <= lenstr; j <<= , mx = p) {
p = ;
for (int i = lenstr - j + ; i <= lenstr; ++i) sec[++p] = i;
for (int i = ; i <= lenstr; ++i) {
if (sa[i] > j)
sec[++p] = sa[i] - j;
}
for (int i = ; i <= mx; ++i) book[i] = ;
for (int i = ; i <= lenstr; ++i) book[fir[sec[i]]]++;
for (int i = ; i <= mx; ++i) book[i] += book[i - ];
for (int i = lenstr; i >= ; --i) sa[book[fir[sec[i]]]--] = sec[i];
// ToChange = fir, fir = sec, sec = ToChange;
swap(fir, sec);
fir[sa[]] = ;
p = ;
for (int i = ; i <= lenstr; ++i) {
fir[sa[i]] = cmp(sec, sa[i - ], sa[i], j) ? p - : p++;
}
}
}
int height[maxn], RANK[maxn];
void calcHight(char str[], int sa[], int lenstr) {
for (int i = ; i <= lenstr; ++i) RANK[sa[i]] = i;
int k = ;
for (int i = ; i <= lenstr - ; ++i) {
k -= k > ;
int j = sa[RANK[i] - ];
while (str[j + k] == str[i + k]) ++k;
height[RANK[i]] = k;
}
}
char str[maxn];
int dp[maxn][];
vector<int> fuck[];
const int need = ;
void init_RMQ(int n, int a[]) {
for (int i = ; i <= n; ++i) {
dp[i][] = a[i];
}
for (int j = ; j < need; ++j) {
for (int i = ; i + ( << j) - <= n; ++i) {
dp[i][j] = min(dp[i][j - ], dp[i + ( << (j - ))][j - ]);
}
}
}
int ask(int be, int en) {
if (be > en) swap(be, en);
be++;
int k = (int)log2(en - be + );
return min(dp[be][k], dp[en - ( << k) + ][k]);
}
int f;
int st[maxn], top;
int t[maxn];
int ans[maxn], id[maxn];
bool check(int pos, int len) {
int res = ask(RANK[st[pos]], RANK[st[top]]);
return res == len;
}
void work() {
memset(ans, , sizeof ans);
memset(id, 0x3f, sizeof id);
scanf("%s", str + );
int lenstr = strlen(str + );
str[lenstr + ] = '$';
str[lenstr + ] = '\0';
da(str, sa, lenstr + , );
calcHight(str, sa, lenstr + );
// for (int i = 1; i <= lenstr + 1; ++i) {
// printf("%d ", sa[i]);
// }
// printf("\n");
init_RMQ(lenstr + , height);
printf("Case #%d:\n", ++f);
top = ;
for (int i = ; i <= lenstr + ; ++i) {
while (top >= && sa[i] < st[top]) {
top--;
}
st[++top] = sa[i];
if (top >= ) {
int tlen = ask(RANK[st[top]], RANK[st[top - ]]);
ans[st[top]] = tlen;
id[st[top]] = st[top - ];
int be = , en = top - ;
while (be <= en) {
int mid = (be + en) >> ;
if (check(mid, tlen)) en = mid - ;
else be = mid + ;
}
id[st[top]] = st[be];
}
}
// for (int i = 1; i <= lenstr; ++i) {
// printf("%d ", ans[i]);
// }
// printf("\n"); top = ;
for (int i = lenstr + ; i >= ; --i) {
while (top >= && sa[i] < st[top]) {
top--;
}
st[++top] = sa[i];
if (top >= ) {
int tlen = ask(RANK[st[top]], RANK[st[top - ]]);
if (ans[st[top]] < tlen) {
ans[st[top]] = tlen;
id[st[top]] = st[top - ];
} else if (ans[st[top]] == tlen && id[st[top]] > st[top - ]) {
id[st[top]] = st[top - ];
} else if (ans[st[top]] > tlen) continue; // 太小了
int be = , en = top - ;
while (be <= en) {
int mid = (be + en) >> ;
if (check(mid, tlen)) en = mid - ;
else be = mid + ;
}
if (check(be, tlen))
id[st[top]] = min(id[st[top]], st[be]);
}
}
// for (int i = 1; i <= lenstr; ++i) {
// printf("%d ", ans[i]);
// }
// printf("\n");
for (int i = ; i <= lenstr;) {
if (ans[i] == ) {
printf("%d %d\n", -, str[i]);
i++;
} else {
printf("%d %d\n", ans[i], id[i] - );
i += ans[i];
}
}
}
int main()
{
#ifdef local
in();
#else
#endif
// printf("%d\n", 1 << 17);
int t;
scanf("%d", &t);
while (t--) work();
return ;
}
或者直接sam一波,sam的建立是在线的,可以不断更新不断弄。
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int N = ;
const int maxn = + ;
struct Node {
int cnt, id, pos; // cnt表示在后缀自动机中从root走到它最多需要多少步
//id表示它是第几个后缀自动机节点,指向了它,但是不知道是第几个,需要id判断
//pos表示它在原串中的位置。
struct Node *pNext[N], *fa;
}suffixAutomaon[maxn * ], *root, *last; //大小需要开2倍,因为有一些虚拟节点
int t; // 用到第几个节点
struct Node *create(int cnt = -, struct Node *node = NULL) { //新的节点
if (cnt != -) {
suffixAutomaon[t].cnt = cnt, suffixAutomaon[t].fa = NULL;
suffixAutomaon[t].id = t;
for (int i = ; i < N; ++i) suffixAutomaon[t].pNext[i] = NULL;
} else {
suffixAutomaon[t] = *node; //保留了node节点指向的信息
suffixAutomaon[t].id = t; //必须要有的,不然id错误
//可能需要注意下pos,在原串中的位置。
// suffixAutomaon[t].pos = su
}
return &suffixAutomaon[t++];
}
void init() {
t = ;
root = last = create(, NULL);
}
void addChar(int x, int pos) { //pos表示在原串的位置
struct Node *p = last, *np = create(p->cnt + , NULL);
np->pos = pos, last = np; //最后一个可接收后缀字符的点。
for (; p != NULL && p->pNext[x] == NULL; p = p->fa) p->pNext[x] = np;
if (p == NULL) {
np->fa = root;
return;
}
struct Node *q = p->pNext[x];
if (q->cnt == p->cnt + ) { //中间没有任何字符
np->fa = q;
return;
}
// p:指向的可以接受后缀的节点
// np:当前插入字符x的新节点
// q:q = p->pNext[x],q就是p中指向的x字符的节点
// nq:因为q->cnt != p->cnt + 1而新建出来的模拟q的节点
struct Node *nq = create(-, q); // 新的q节点,用来代替q,帮助np接收后缀字符
nq->cnt = p->cnt + ; //就是需要这样,这样中间不包含任何字符
q->fa = nq, np->fa = nq; //现在nq是包含了本来q的所有指向信息
for (; p && p->pNext[x] == q; p = p->fa) {
p->pNext[x] = nq;
}
}
void build(char str[], int lenstr) {
init();
for (int i = ; i <= lenstr; ++i) addChar(str[i] - 'a', i);
}
char str[maxn];
int f;
void work() {
scanf("%s", str + );
int lenstr = strlen(str + );
printf("Case #%d:\n", ++f);
init();
for (int i = ; i <= lenstr;) {
int now = , len = ;
for (; i <= lenstr && suffixAutomaon[now].pNext[str[i] - 'a']; ++i, ++len) {
now = suffixAutomaon[now].pNext[str[i] - 'a']->id;
addChar(str[i] - 'a', i);
}
if (len) {
printf("%d %d\n", len, suffixAutomaon[now].pos - len);
} else {
printf("-1 %d\n", str[i]);
addChar(str[i] - 'a', i);
i++;
}
}
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
int t;
scanf("%d", &t);
while (t--) work();
return ;
}
HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分的更多相关文章
- HDU 5558 Alice's Classified Message(后缀数组+二分+rmq(+线段树?))
题意 大概就是给你一个串,对于每个\(i\),在\([1,i-1]\)中找到一个\(j\),使得\(lcp(i,j)\)最长,若有多个最大\(j\)选最小,求\(j\)和这个\(lcp\)长度 思路 ...
- (HDU 5558) 2015ACM/ICPC亚洲区合肥站---Alice's Classified Message(后缀数组)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=5558 Problem Description Alice wants to send a classi ...
- 【BZOJ-3238】差异 后缀数组 + 单调栈
3238: [Ahoi2013]差异 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1561 Solved: 734[Submit][Status] ...
- BZOJ_3879_SvT_后缀数组+单调栈
BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...
- BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈
BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...
- BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)
BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...
- 【BZOJ3879】SvT 后缀数组+单调栈
[BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...
- BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】
题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...
- BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】
题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...
随机推荐
- Dennis Gabor与全息摄影
Dennis Gabor:1900年6月5日出生在匈牙利首都布达佩斯,1971年诺贝尔物理学奖授予英国伦敦帝国科技学院的匈牙利裔物理学家伽博(Dennis Gabor,1900-1979),以表彰他发 ...
- 【2008nmj】BP二元分类
在人的大脑里有数以万计的神经元,它们之间通过神经突触来连接.用以判断. BP神经网络 MATLAB实现:
- MVC上的jsonp扩展,解决跨域访问问题
总是有人会遇到跨域问题,然后有个jsonp的解决方案,MVC中代码如下: public class JsonpResult : System.Web.Mvc.JsonResult { object d ...
- 上课总结-模电chapter 1
Chapter 1 半导体器件 一.杂质半导体(N型半导体/P型半导体) N型 p型 特点 N型 将少量5价元素参入四价元素中==> 电子多 ==>电子为多子 p型 将少量3价元素参入四价 ...
- 可以避免的10大IT面试误区
在所有的面试过程当中,很多面试者都普遍犯了这样一个错误——由于对面试准备的不足,所以对于应聘公司的背景,以及一些最基本的信息都缺乏了解,有些面试者甚至对要面试的公司毫无所知.这些数据并不是空穴来风,A ...
- 连接数据库+注册->登录->抽奖(有关联关系的接口)
注册账号信息需要写入数据库,登录和抽奖时从数据库获取数据 一.连接数据库 my_sql.py: import pymysql class MyDb: def __init__(self,host,pa ...
- LoadRunner11_录制脚本时的浏览器版本
最近在做的项目,因为浏览器类型及版本问题,浪费了很多时间,现在就此次的小折腾做一个小结,方便后期录制时选择(如果自己写脚本,那就可以完全忽略这些啦): (1)IE:win7 32位LR11.0,最 ...
- SqlServer批量插入(SqlBulkCopy、表值参数)
之前做项目需要用到数据库的批量插入,于是就研究了一下,现在做个总结. 创建了一个用来测试的Student表: CREATE TABLE [dbo].[Student]( [ID] [int] PRIM ...
- 启动Tomcat报错
如果发现引入jar包有问题时,看jar包是否损坏,变成了0kb.如果是这样,在网上试尽解决办法也是有问题的. 一般Tomcat启动报错,要引入这两个jar包.
- url 路径的拼接
url 路径的拼接 刚开始做项目总是被路径所困扰,不知道何时该拼接,何时不拼接,怎么拼接,如何拼接,有像地址栏拼接一样的,又在跳转页面拼接的,还有在 a 标签中 href 中拼接的 ,当时做的时候一 ...