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表】
题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...
随机推荐
- 通过fork函数创建进程的跟踪,分析linux内核进程的创建
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验过程 1.打开gdb, ...
- 《PRC:更新项目汇总额》报错
请求报红,日志如下: +---------------------------------------------------------------------------+ 项目: Version ...
- Arduino ADC + 模拟温度传感器LM35D
LM35是美国国家半导体(后被TI收购)推出的精密温度传感IC系列,其信号输出方式为模拟输出,输出电压值与摄氏温度值呈正比,且用户不需额外的校正就能获得较高的测量精度.其主要特性有: 供电电压:4~3 ...
- duilib入门简明教程 -- 第一个程序 Hello World(3)
小伙伴们有点迫不及待了么,来看一看Hello World吧: 新建一个空的win32项目,新建一个main.cpp文件,将以下代码复制进去: #include <windows.h> #i ...
- Log--日志变大原因总结
1. 有产生大日志操作,如重建整理索引,大量数据修改等2. 长期未提交事务,为保证为提交事务可以回滚,从最早为提交事务开始之后的所有事务,都是活动事务,不能被截断或覆盖3. 日志没有定期备份4. 镜像 ...
- mvc 高并发解决方案之一---存储过程
MVC用户访问多线程,一般的lock是无法造成单例的. 存储过程既是一种解决方案,先来看看存储过程优缺点: A. 存储过程允许标准组件式编程 存储过程创建后可以在程序中被多次调用执行,而不必重新编写该 ...
- angular 路由传参
第一种:<a [routerLink]="['/product']" [queryParams]="{id: 1}">商品详情</a> ...
- sqlServer组合主键
sqlServer 组合主键 创建表时: create table Person ( Name1 ) not null ,Name2 ) not null primary key(Name1,Na ...
- C# 抽象(3)
接上章: 抽象类中有抽象方法,那么可不可以有非抽象方法呢? 答案是可以的. abstract class Human { public abstract void Think(); public ab ...
- 【ARC074F】Lotus Leaves 最小割
Description 给你一个n*m网格图,有起点荷叶和终点荷叶,有中转荷叶,其他的格子没东西,一个荷叶可以跳到同一行或者列的另一个荷叶.问最多删掉几个中转荷叶能让起点终点不连通.如果不行输出-1. ...