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

总共13题。

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

suffix(后缀数组的应用)

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

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

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

模板:加上RMQ操作

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <math.h>
  4. #include <algorithm>


  5. using namespace std;

  6. const int maxn = +;

  7. int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
  8. int sa[maxn];
  9. int r[maxn];

  10. int cmp(int *r,int a,int b,int l)
  11. {
  12. return r[a]==r[b]&&r[a+l]==r[b+l];
  13. }
  14. void da(int *r,int *sa,int n,int m)
  15. {
  16. int i,j,p,*x=wa,*y=wb,*t;
  17. for(i=; i<m; i++) ws[i]=;
  18. for(i=; i<n; i++) ws[x[i]=r[i]]++;
  19. for(i=; i<m; i++) ws[i]+=ws[i-];
  20. for(i=n-; i>=; i--) sa[--ws[x[i]]]=i;
  21. for(j=,p=; p<n; j*=,m=p)
  22. {
  23. for(p=,i=n-j; i<n; i++) y[p++]=i;
  24. for(i=; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
  25. for(i=; i<n; i++) wv[i]=x[y[i]];
  26. for(i=; i<m; i++) ws[i]=;
  27. for(i=; i<n; i++) ws[wv[i]]++;
  28. for(i=; i<m; i++) ws[i]+=ws[i-];
  29. for(i=n-; i>=; i--) sa[--ws[wv[i]]]=y[i];
  30. for(t=x,x=y,y=t,p=,x[sa[]]=,i=; i<n; i++)
  31. x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
  32. }
  33. return;
  34. }
  35. int ranks[maxn],height[maxn];
  36. void calheight(int *r,int *sa,int n)
  37. {
  38. int i,j,k=;
  39. for(i=; i<=n; i++) ranks[sa[i]]=i;
  40. for(i=; i<n; height[ranks[i++]]=k)
  41. for(k?k--:,j=sa[ranks[i]-]; r[i+k]==r[j+k]; k++);
  42. return;
  43. }


  44. char str[maxn];

  45. int f[maxn][];
  46. void init(int len) {
  47. for(int i = ; i <= len; i++) f[i][] = height[i];
  48. for(int s = ; (<<s)<=len; s++) {
  49. int tmp = (<<s);
  50. for(int i = ; i+tmp-<=len; i++) {
  51. f[i][s] = min(f[i][s-],f[i+tmp/][s-]);
  52. }
  53. }
  54. }

  55. int cal(int l,int r) {
  56. int len = log2(r-l+);
  57. int ans = min(f[l][len],f[r-(<<len)+][len]);
  58. return ans;
  59. }



  60. int main()
  61. {
  62. freopen("in.txt","r",stdin);

  63. scanf("%s",str);
  64. int n = strlen(str);

  65. for(int i = ; i < n; i++)
  66. r[i] = str[i] - 'a' + ;
  67. da(r,sa,n+,);
  68. calheight(r,sa,n);

  69. for(int i = ; i <= n; i++) {
  70. printf("%d ",sa[i]);
  71. }
  72. puts("");

  73. for(int i = ; i < n; i++) {
  74. printf("%d ",ranks[i]);
  75. }
  76. puts("");

  77. for(int i = ; i <= n; i++) {
  78. printf("%d ",height[i]);
  79. }
  80. puts("");

  81. init(n);

  82. //height 上的 RMQ(); 从 2开始;
  83. printf("%d\n",cal(,));
  84. return ;
  85. }
  1.  

例题一:最长公共前缀

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

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

  1.  
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <math.h>
  4. #include <algorithm>


  5. using namespace std;

  6. const int maxn = +;

  7. int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
  8. int sa[maxn];
  9. int r[maxn];

  10. int cmp(int *r,int a,int b,int l)
  11. {
  12. return r[a]==r[b]&&r[a+l]==r[b+l];
  13. }
  14. void da(int *r,int *sa,int n,int m)
  15. {
  16. int i,j,p,*x=wa,*y=wb,*t;
  17. for(i=; i<m; i++) ws[i]=;
  18. for(i=; i<n; i++) ws[x[i]=r[i]]++;
  19. for(i=; i<m; i++) ws[i]+=ws[i-];
  20. for(i=n-; i>=; i--) sa[--ws[x[i]]]=i;
  21. for(j=,p=; p<n; j*=,m=p)
  22. {
  23. for(p=,i=n-j; i<n; i++) y[p++]=i;
  24. for(i=; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
  25. for(i=; i<n; i++) wv[i]=x[y[i]];
  26. for(i=; i<m; i++) ws[i]=;
  27. for(i=; i<n; i++) ws[wv[i]]++;
  28. for(i=; i<m; i++) ws[i]+=ws[i-];
  29. for(i=n-; i>=; i--) sa[--ws[wv[i]]]=y[i];
  30. for(t=x,x=y,y=t,p=,x[sa[]]=,i=; i<n; i++)
  31. x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
  32. }
  33. return;
  34. }
  35. int ranks[maxn],height[maxn];
  36. void calheight(int *r,int *sa,int n)
  37. {
  38. int i,j,k=;
  39. for(i=; i<=n; i++) ranks[sa[i]]=i;
  40. for(i=; i<n; height[ranks[i++]]=k)
  41. for(k?k--:,j=sa[ranks[i]-]; r[i+k]==r[j+k]; k++);
  42. return;
  43. }


  44. char str[maxn];

  45. int f[maxn][];
  46. void init(int len) {
  47. for(int i = ; i <= len; i++) f[i][] = height[i];
  48. for(int s = ; (<<s)<=len; s++) {
  49. int tmp = (<<s);
  50. for(int i = ; i+tmp-<=len; i++) {
  51. f[i][s] = min(f[i][s-],f[i+tmp/][s-]);
  52. }
  53. }
  54. }

  55. int cal(int l,int r) {
  56. int len = log2(r-l+);
  57. int ans = min(f[l][len],f[r-(<<len)+][len]);
  58. return ans;
  59. }



  60. int main()
  61. {
  62. freopen("in.txt","r",stdin);

  63. scanf("%s",str);
  64. int n = strlen(str);

  65. for(int i = ; i < n; i++)
  66. r[i] = str[i] - 'a' + ;
  67. da(r,sa,n+,);
  68. calheight(r,sa,n);

  69. for(int i = ; i <= n; i++) {
  70. printf("%d ",sa[i]);
  71. }
  72. puts("");

  73. for(int i = ; i < n; i++) {
  74. printf("%d ",ranks[i]);
  75. }
  76. puts("");

  77. for(int i = ; i <= n; i++) {
  78. printf("%d ",height[i]);
  79. }
  80. puts("");

  81. init(n);

  82. int l,r; // 询问后缀L,R的最长公共前缀,从0开始

  83. scanf("%d%d",&l,&r);
  84. l = ranks[l];
  85. r = ranks[r];

  86. if(l>r) swap(l,r);

  87. l++;
  88. printf("%d\n",cal(l,r));

  89. return ;
  90. }

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

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

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

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

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

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

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

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

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

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

  1.  
  1. int main()
  2. {
  3. freopen("in.txt","r",stdin);

  4. scanf("%s",str);
  5. int n = strlen(str);

  6. for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;

  7. da(r,sa,n+,);
  8. calheight(r,sa,n);

  9. int l = ,r= n;
  10. int ans = ;
  11. while(l<=r) {
  12. int mid = l + (r-l)/;
  13. int L = inf,R= -inf;
  14. bool flag = false;
  15. for(int i = ; i <= n; i++) {
  16. if(height[i]>=mid) { //按照 mid 分组
  17. L = min(L,sa[i]);
  18. L = min(L,sa[i-]);
  19. R = max(R,sa[i]);
  20. R = max(R,sa[i-]);
  21. }
  22. else
  23. {
  24. if(L+mid+<=R) {
  25. flag = true;
  26. ans = mid;
  27. }
  28. L = inf;
  29. R = -inf;
  30. }
  31. }

  32. if(L+mid+<=R) flag = true;
  33. if(flag) l = mid+;
  34. else r = mid - ;
  35. }

  36. printf("%d\n",ans);

  37. return ;
  38. }

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

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

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

  1.  
  1. int main()
  2. {
  3. freopen("in.txt","r",stdin);

  4. scanf("%s",str);
  5. int n = strlen(str);
  6. int k;
  7. scanf("%d",&k);

  8. for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;

  9. da(r,sa,n+,);
  10. calheight(r,sa,n);

  11. for(int i = ; i<= n;i++)
  12. printf("%d ",height[i]);
  13. puts("");

  14. int l = ,r= n;
  15. int ans = ;
  16. while(l<=r) {
  17. int mid = l + (r-l)/;
  18. int L = inf,R= -inf;
  19. bool flag = false;
  20. int cnt = ;
  21. for(int i = ; i <= n; i++) {
  22. if(height[i]>=mid) { //按照 mid 分组
  23. cnt++;
  24. }
  25. else
  26. {
  27. if(cnt>=k) {
  28. flag = true;
  29. ans = mid;
  30. }
  31. L = inf;
  32. R = -inf;
  33. cnt = ;
  34. }
  35. }
  36. if(flag) l = mid+;
  37. else r = mid - ;
  38. }

  39. printf("%d\n",ans);

  40. return ;
  41. }

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

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

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

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

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

  1.  
  1. int main()
  2. {
  3. freopen("in.txt","r",stdin);

  4. scanf("%s",str);
  5. int n = strlen(str);

  6. for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;

  7. da(r,sa,n+,);
  8. calheight(r,sa,n);

  9. for(int i = ; i<= n;i++)
  10. printf("%d ",height[i]);
  11. puts("");

  12. int cnt = n - sa[];

  13. for(int i = ; i <= n; i++)
  14. cnt = cnt + n - sa[i] - height[i];
  15. printf("%d\n",cnt);

  16. return ;
  17. }
  1.  

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

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

先搞一发Manacher;

  1. #include <bits/stdc++.h>
  2. using namespace std;

  3. const int maxn = ;

  4. char instr[maxn],str[maxn*];
  5. int rad[maxn*];


  6. int Manacher()
  7. {
  8. int i,j,maxx;
  9. int n = strlen(instr);
  10. memset(str,'#',sizeof(str));
  11. for(i=;i<n;i++)
  12. str[(i+)<<] = instr[i];

  13. n = (n+)<<;
  14. str[n] = '$';
  15. int maxRad;
  16. maxRad = j = maxx = ;
  17. for(i = ;i<n;i++)
  18. {
  19. if(i<maxx)
  20. rad[i] = min(rad[*j-i],maxx-i);
  21. else rad[i] = ;

  22. while(str[i-rad[i]]==str[i+rad[i]])
  23. rad[i] ++;
  24. if(maxRad<rad[i])
  25. maxRad = rad[i];
  26. if(rad[i]+i>maxx)
  27. {
  28. j = i;
  29. maxx = rad[i] + i;
  30. }

  31. }
  32. return maxRad;

  33. }

  34. int main()
  35. {
  36. int t;
  37. scanf("%d",&t);
  38. while(t--)
  39. {
  40. scanf("%s",instr);
  41. printf("%d\n",Manacher()-);
  42. }
  43. return ;
  44. }

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

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

  1. int main()
  2. {
  3. freopen("in.txt","r",stdin);

  4. scanf("%s",str);

  5. int n = strlen(str);
  6. int tmpn = n;
  7. str[n] = '$';

  8. for(int i = n+,j=; i <= *n; i++,j++)
  9. str[i] = str[n--j];

  10. puts(str);

  11. for(int i = ; i < n; i++) r[i] = str[i] - 'a' + ;
  12. r[n] = ;
  13. for(int i = n+; i < *n+; i++) r[i] = str[i] - 'a' + ;
  14. n = strlen(str);
  15. //
  16. // da(r,sa,n+1,300);
  17. // calheight(r,sa,n);
  18. //
  19. // for(int i =2 ; i<= n;i++)
  20. // printf("%d ",height[i]);
  21. // puts("");


  22. da(r,sa,n,);
  23. calheight(r,sa,n);

  24. for(int i = ; i <= n; i++)
  25. printf("%d ",sa[i]);
  26. puts("");

  27. init(n);

  28. int ans = ;
  29. for(int i = ; i < tmpn; i++) { //枚举中心
  30. int l = i;
  31. int r = n - i - ;
  32. l = ranks[l];
  33. r = ranks[r];

  34. l++;

  35. int lcp = cal(l,r); //回文是奇数
  36. if(lcp*->ans) {
  37. ans = lcp * -;
  38. }

  39. l = i;
  40. if(l!=tmpn) {
  41. l++;
  42. r = n - l - ;
  43. l = ranks[l];
  44. r = ranks[r];
  45. l++;
  46. lcp = cal(l,r);
  47. ans = max(ans,lcp*);
  48. }


  49. }
  50. printf("%d\n",ans);


  51. return ;
  52. }

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. 理解fig,ax = plt.subplots()

    fig = plt.figure() ax = fig.add_subplot(1,1,1) fig, ax = plt.subplots(1,3),其中参数1和3分别代表子图的行数和列数,一共有 1 ...

  2. Java开发环境搭建——IntelliJ Idea开发环境

    IntelliJ Idea版本选择由于公司使用JDK7,所以我选择安装Version 2016.1.4(手动安装试验出来的,最新版的2016.1.4启动时提示需要安装JDK8)下载 前面说明有误,其实 ...

  3. go语言解析网页利器goquery使用教程(爬虫必备)

    某些时候需要爬取网页中指定信息时,通常需要一些框架解析网页行成dom模型,然后来操作节点来获取相应的信息.在java中很显然就是Jsoup,而在Golang里,应该就是这个goquery了吧. goq ...

  4. CountDownLatch 多线程,等待所有线程结束

    CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 主要方法 public CountDownLatch(int count); 构造 ...

  5. CVE-2018-7600-Drupal远程代码执行漏洞-Render API

    今天学习一下Drupal的另一个漏洞,由于渲染数组不当造成的漏洞 poc: url:http://localhost/drupal-8.5.0/user/register?element_parent ...

  6. 微信小程序开发踩坑记录

    1.由于小程序wx.request()方法是异步的,在app.js执行ajax后,各分页加载app.js的全局数据时,无法按顺序加载.例: //app.js App({ ajax:function() ...

  7. 【tomcat】关于tomcat的使用:将tomcat加入系统服务列表

    一.下载TOMCAT 选择合适的版本进行下载: http://tomcat.apache.org/ 解压zip文件得到tomcat目录: 二.添加CATALINA_HOME到环境变量 service. ...

  8. A bug about RecipientEditTextView

    - Steps to reproduce the problem. Pre-condition:the threshold of the RecipientEditTextView is set to ...

  9. easyui datebox 精确到秒并且显示值

    其实这个官网文档有的,也就不啰嗦了,直接贴官网的代码吧. <input id="dt" type="text" name="birthday&q ...

  10. XMLHttpRequest.responseType

    "arraybuffer" "blob" "document" "json" "text"