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存在单调性,单调递增,所以可以二分。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include <vector>
  7. #include <queue>
  8. #include <string>
  9. #include <stack>
  10. #include <map>
  11. #include <set>
  12. #include <bitset>
  13. #define X first
  14. #define Y second
  15. #define clr(u,v); memset(u,v,sizeof(u));
  16. #define in() freopen("data.txt","r",stdin);
  17. #define out() freopen("ans","w",stdout);
  18. #define Clear(Q); while (!Q.empty()) Q.pop();
  19. #define pb push_back
  20. #define inf (0x3f3f3f3f)
  21. using namespace std;
  22. typedef long long ll;
  23. typedef pair<int, int> pii;
  24. const int INF = 0x3f3f3f3f;
  25. const int maxn = + ;
  26. int sa[maxn], x[maxn], y[maxn], book[maxn];
  27. bool cmp(int r[], int a, int b, int len) {
  28. return r[a] == r[b] && r[a + len] == r[b + len];
  29. }
  30. void da(char str[], int sa[], int lenstr, int mx) {
  31. int *fir = x, *sec = y, *ToChange;
  32. for (int i = ; i <= mx; ++i) book[i] = ;
  33. for (int i = ; i <= lenstr; ++i) {
  34. fir[i] = str[i];
  35. book[str[i]]++;
  36. }
  37. for (int i = ; i <= mx; ++i) book[i] += book[i - ];
  38. for (int i = lenstr; i >= ; --i) sa[book[fir[i]]--] = i;
  39. for (int j = , p = ; p <= lenstr; j <<= , mx = p) {
  40. p = ;
  41. for (int i = lenstr - j + ; i <= lenstr; ++i) sec[++p] = i;
  42. for (int i = ; i <= lenstr; ++i) {
  43. if (sa[i] > j)
  44. sec[++p] = sa[i] - j;
  45. }
  46. for (int i = ; i <= mx; ++i) book[i] = ;
  47. for (int i = ; i <= lenstr; ++i) book[fir[sec[i]]]++;
  48. for (int i = ; i <= mx; ++i) book[i] += book[i - ];
  49. for (int i = lenstr; i >= ; --i) sa[book[fir[sec[i]]]--] = sec[i];
  50. // ToChange = fir, fir = sec, sec = ToChange;
  51. swap(fir, sec);
  52. fir[sa[]] = ;
  53. p = ;
  54. for (int i = ; i <= lenstr; ++i) {
  55. fir[sa[i]] = cmp(sec, sa[i - ], sa[i], j) ? p - : p++;
  56. }
  57. }
  58. }
  59. int height[maxn], RANK[maxn];
  60. void calcHight(char str[], int sa[], int lenstr) {
  61. for (int i = ; i <= lenstr; ++i) RANK[sa[i]] = i;
  62. int k = ;
  63. for (int i = ; i <= lenstr - ; ++i) {
  64. k -= k > ;
  65. int j = sa[RANK[i] - ];
  66. while (str[j + k] == str[i + k]) ++k;
  67. height[RANK[i]] = k;
  68. }
  69. }
  70. char str[maxn];
  71. int dp[maxn][];
  72. vector<int> fuck[];
  73. const int need = ;
  74. void init_RMQ(int n, int a[]) {
  75. for (int i = ; i <= n; ++i) {
  76. dp[i][] = a[i];
  77. }
  78. for (int j = ; j < need; ++j) {
  79. for (int i = ; i + ( << j) - <= n; ++i) {
  80. dp[i][j] = min(dp[i][j - ], dp[i + ( << (j - ))][j - ]);
  81. }
  82. }
  83. }
  84. int ask(int be, int en) {
  85. if (be > en) swap(be, en);
  86. be++;
  87. int k = (int)log2(en - be + );
  88. return min(dp[be][k], dp[en - ( << k) + ][k]);
  89. }
  90. int f;
  91. int st[maxn], top;
  92. int t[maxn];
  93. int ans[maxn], id[maxn];
  94. bool check(int pos, int len) {
  95. int res = ask(RANK[st[pos]], RANK[st[top]]);
  96. return res == len;
  97. }
  98. void work() {
  99. memset(ans, , sizeof ans);
  100. memset(id, 0x3f, sizeof id);
  101. scanf("%s", str + );
  102. int lenstr = strlen(str + );
  103. str[lenstr + ] = '$';
  104. str[lenstr + ] = '\0';
  105. da(str, sa, lenstr + , );
  106. calcHight(str, sa, lenstr + );
  107. // for (int i = 1; i <= lenstr + 1; ++i) {
  108. // printf("%d ", sa[i]);
  109. // }
  110. // printf("\n");
  111. init_RMQ(lenstr + , height);
  112. printf("Case #%d:\n", ++f);
  113. top = ;
  114. for (int i = ; i <= lenstr + ; ++i) {
  115. while (top >= && sa[i] < st[top]) {
  116. top--;
  117. }
  118. st[++top] = sa[i];
  119. if (top >= ) {
  120. int tlen = ask(RANK[st[top]], RANK[st[top - ]]);
  121. ans[st[top]] = tlen;
  122. id[st[top]] = st[top - ];
  123. int be = , en = top - ;
  124. while (be <= en) {
  125. int mid = (be + en) >> ;
  126. if (check(mid, tlen)) en = mid - ;
  127. else be = mid + ;
  128. }
  129. id[st[top]] = st[be];
  130. }
  131. }
  132. // for (int i = 1; i <= lenstr; ++i) {
  133. // printf("%d ", ans[i]);
  134. // }
  135. // printf("\n");
  136.  
  137. top = ;
  138. for (int i = lenstr + ; i >= ; --i) {
  139. while (top >= && sa[i] < st[top]) {
  140. top--;
  141. }
  142. st[++top] = sa[i];
  143. if (top >= ) {
  144. int tlen = ask(RANK[st[top]], RANK[st[top - ]]);
  145. if (ans[st[top]] < tlen) {
  146. ans[st[top]] = tlen;
  147. id[st[top]] = st[top - ];
  148. } else if (ans[st[top]] == tlen && id[st[top]] > st[top - ]) {
  149. id[st[top]] = st[top - ];
  150. } else if (ans[st[top]] > tlen) continue; // 太小了
  151. int be = , en = top - ;
  152. while (be <= en) {
  153. int mid = (be + en) >> ;
  154. if (check(mid, tlen)) en = mid - ;
  155. else be = mid + ;
  156. }
  157. if (check(be, tlen))
  158. id[st[top]] = min(id[st[top]], st[be]);
  159. }
  160. }
  161. // for (int i = 1; i <= lenstr; ++i) {
  162. // printf("%d ", ans[i]);
  163. // }
  164. // printf("\n");
  165. for (int i = ; i <= lenstr;) {
  166. if (ans[i] == ) {
  167. printf("%d %d\n", -, str[i]);
  168. i++;
  169. } else {
  170. printf("%d %d\n", ans[i], id[i] - );
  171. i += ans[i];
  172. }
  173. }
  174. }
  175. int main()
  176. {
  177. #ifdef local
  178. in();
  179. #else
  180. #endif
  181. // printf("%d\n", 1 << 17);
  182. int t;
  183. scanf("%d", &t);
  184. while (t--) work();
  185. return ;
  186. }

或者直接sam一波,sam的建立是在线的,可以不断更新不断弄。

  1. #include <bits/stdc++.h>
  2. #define IOS ios::sync_with_stdio(false)
  3. using namespace std;
  4. #define inf (0x3f3f3f3f)
  5. typedef long long int LL;
  6. const int N = ;
  7. const int maxn = + ;
  8. struct Node {
  9. int cnt, id, pos; // cnt表示在后缀自动机中从root走到它最多需要多少步
  10. //id表示它是第几个后缀自动机节点,指向了它,但是不知道是第几个,需要id判断
  11. //pos表示它在原串中的位置。
  12. struct Node *pNext[N], *fa;
  13. }suffixAutomaon[maxn * ], *root, *last; //大小需要开2倍,因为有一些虚拟节点
  14. int t; // 用到第几个节点
  15. struct Node *create(int cnt = -, struct Node *node = NULL) { //新的节点
  16. if (cnt != -) {
  17. suffixAutomaon[t].cnt = cnt, suffixAutomaon[t].fa = NULL;
  18. suffixAutomaon[t].id = t;
  19. for (int i = ; i < N; ++i) suffixAutomaon[t].pNext[i] = NULL;
  20. } else {
  21. suffixAutomaon[t] = *node; //保留了node节点指向的信息
  22. suffixAutomaon[t].id = t; //必须要有的,不然id错误
  23. //可能需要注意下pos,在原串中的位置。
  24. // suffixAutomaon[t].pos = su
  25. }
  26. return &suffixAutomaon[t++];
  27. }
  28. void init() {
  29. t = ;
  30. root = last = create(, NULL);
  31. }
  32. void addChar(int x, int pos) { //pos表示在原串的位置
  33. struct Node *p = last, *np = create(p->cnt + , NULL);
  34. np->pos = pos, last = np; //最后一个可接收后缀字符的点。
  35. for (; p != NULL && p->pNext[x] == NULL; p = p->fa) p->pNext[x] = np;
  36. if (p == NULL) {
  37. np->fa = root;
  38. return;
  39. }
  40. struct Node *q = p->pNext[x];
  41. if (q->cnt == p->cnt + ) { //中间没有任何字符
  42. np->fa = q;
  43. return;
  44. }
  45. // p:指向的可以接受后缀的节点
  46. // np:当前插入字符x的新节点
  47. // q:q = p->pNext[x],q就是p中指向的x字符的节点
  48. // nq:因为q->cnt != p->cnt + 1而新建出来的模拟q的节点
  49. struct Node *nq = create(-, q); // 新的q节点,用来代替q,帮助np接收后缀字符
  50. nq->cnt = p->cnt + ; //就是需要这样,这样中间不包含任何字符
  51. q->fa = nq, np->fa = nq; //现在nq是包含了本来q的所有指向信息
  52. for (; p && p->pNext[x] == q; p = p->fa) {
  53. p->pNext[x] = nq;
  54. }
  55. }
  56. void build(char str[], int lenstr) {
  57. init();
  58. for (int i = ; i <= lenstr; ++i) addChar(str[i] - 'a', i);
  59. }
  60. char str[maxn];
  61. int f;
  62. void work() {
  63. scanf("%s", str + );
  64. int lenstr = strlen(str + );
  65. printf("Case #%d:\n", ++f);
  66. init();
  67. for (int i = ; i <= lenstr;) {
  68. int now = , len = ;
  69. for (; i <= lenstr && suffixAutomaon[now].pNext[str[i] - 'a']; ++i, ++len) {
  70. now = suffixAutomaon[now].pNext[str[i] - 'a']->id;
  71. addChar(str[i] - 'a', i);
  72. }
  73. if (len) {
  74. printf("%d %d\n", len, suffixAutomaon[now].pos - len);
  75. } else {
  76. printf("-1 %d\n", str[i]);
  77. addChar(str[i] - 'a', i);
  78. i++;
  79. }
  80. }
  81. }
  82.  
  83. int main() {
  84. #ifdef local
  85. freopen("data.txt", "r", stdin);
  86. // freopen("data.txt", "w", stdout);
  87. #endif
  88. int t;
  89. scanf("%d", &t);
  90. while (t--) work();
  91. return ;
  92. }

HUID 5558 Alice's Classified Message 后缀数组+单调栈+二分的更多相关文章

  1. HDU 5558 Alice's Classified Message(后缀数组+二分+rmq(+线段树?))

    题意 大概就是给你一个串,对于每个\(i\),在\([1,i-1]\)中找到一个\(j\),使得\(lcp(i,j)\)最长,若有多个最大\(j\)选最小,求\(j\)和这个\(lcp\)长度 思路 ...

  2. (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 ...

  3. 【BZOJ-3238】差异 后缀数组 + 单调栈

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1561  Solved: 734[Submit][Status] ...

  4. BZOJ_3879_SvT_后缀数组+单调栈

    BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...

  5. BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈

    BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...

  6. BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)

    BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...

  7. 【BZOJ3879】SvT 后缀数组+单调栈

    [BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...

  8. BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】

    题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...

  9. BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】

    题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...

随机推荐

  1. 通过fork函数创建进程的跟踪,分析linux内核进程的创建

    作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验过程 1.打开gdb, ...

  2. 《PRC:更新项目汇总额》报错

    请求报红,日志如下: +---------------------------------------------------------------------------+ 项目: Version ...

  3. Arduino ADC + 模拟温度传感器LM35D

    LM35是美国国家半导体(后被TI收购)推出的精密温度传感IC系列,其信号输出方式为模拟输出,输出电压值与摄氏温度值呈正比,且用户不需额外的校正就能获得较高的测量精度.其主要特性有: 供电电压:4~3 ...

  4. duilib入门简明教程 -- 第一个程序 Hello World(3)

    小伙伴们有点迫不及待了么,来看一看Hello World吧: 新建一个空的win32项目,新建一个main.cpp文件,将以下代码复制进去: #include <windows.h> #i ...

  5. Log--日志变大原因总结

    1. 有产生大日志操作,如重建整理索引,大量数据修改等2. 长期未提交事务,为保证为提交事务可以回滚,从最早为提交事务开始之后的所有事务,都是活动事务,不能被截断或覆盖3. 日志没有定期备份4. 镜像 ...

  6. mvc 高并发解决方案之一---存储过程

    MVC用户访问多线程,一般的lock是无法造成单例的. 存储过程既是一种解决方案,先来看看存储过程优缺点: A. 存储过程允许标准组件式编程 存储过程创建后可以在程序中被多次调用执行,而不必重新编写该 ...

  7. angular 路由传参

    第一种:<a [routerLink]="['/product']" [queryParams]="{id: 1}">商品详情</a> ...

  8. sqlServer组合主键

    sqlServer   组合主键 创建表时: create table Person ( Name1 ) not null ,Name2 ) not null primary key(Name1,Na ...

  9. C# 抽象(3)

    接上章: 抽象类中有抽象方法,那么可不可以有非抽象方法呢? 答案是可以的. abstract class Human { public abstract void Think(); public ab ...

  10. 【ARC074F】Lotus Leaves 最小割

    Description 给你一个n*m网格图,有起点荷叶和终点荷叶,有中转荷叶,其他的格子没东西,一个荷叶可以跳到同一行或者列的另一个荷叶.问最多删掉几个中转荷叶能让起点终点不连通.如果不行输出-1. ...