SPOJ687 Repeats

本篇是罗穗骞《后缀数组——处理字符串的有力工具》的读书笔记。

知识点:  后缀数组、RMQ

解题思路:

  枚举长度 \(L\),然后检查长度为 \(L\) 的子串最多能连续重复几次。

  对于给定的字符串 \(S\),我们只关注其上坐标为 \(0, L, 2L, ......\) 的点。

  如果连续重复子串的首字符恰好在这些点上,那么连续重复子串出现的次数恰好是 \( \frac{lcp(L_1, L_2)}{L} + 1\),(注:\(lcp\) 为 Longest Common Prefix 的简写),如图 1 所示;

  否则,我们先计算出 \(lcp(L_1, L_2)\) 中 模 \( L\) 后余下的长度 \(L'\),如图 2 中橙色圈里的片段,可以推测出连续重复子串真正的首字符位于 \( pos = L_1 - (L - L')\),如果 \(0 \le pos\),则连续重复子串出现的次数为:\( \frac{lcp(pos, pos+L)}{L} + 1\)。

  记录出现的最多次数,即为答案。最长公共前缀的查询要用 RMQ 优化。

AC代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 
  5 using namespace std;
  6 const int maxn = 50000 + 5, inf = 0x7fffffff;
  7 int len, tk;
  8 int Rank[maxn], tmp[maxn];
  9 int S[maxn];
 10 int sa[maxn], lcp[maxn];
 11 
 12 bool compare_sa(int i, int j) {
 13     if (Rank[i] != Rank[j])    return Rank[i] < Rank[j];
 14     else {
 15         int ri = i + tk <= len ? Rank[i + tk] : -1;
 16         int rj = j + tk <= len ? Rank[j + tk] : -1;
 17         return ri < rj;
 18     }
 19 }
 20 
 21 void construct_sa() {
 22     for (int i = 0; i <= len; i++) {
 23         sa[i] = i;
 24         Rank[i] = i < len ? S[i] : -1;
 25     }
 26 
 27     for (tk = 1; tk <= len; tk *= 2) {
 28         sort(sa, sa + len + 1, compare_sa);
 29         tmp[sa[0]] = 0;
 30         for (int i = 1; i <= len; i++) {
 31             tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
 32         }
 33         for (int i = 0; i <= len; i++) {
 34             Rank[i] = tmp[i];
 35         }
 36     }
 37 }
 38 
 39 void construct_lcp() {
 40     int h = 0;
 41     lcp[0] = 0;
 42     for (int i = 0; i < len; i++) {
 43         int j = sa[Rank[i] - 1];
 44 
 45         if (h > 0)    h--;
 46         for (; j + h < len && i + h < len; h++) {
 47             if (S[j + h] != S[i + h])    break;
 48         }
 49         lcp[Rank[i] - 1] = h;
 50     }
 51 }
 52 
 53 int RMQ[maxn];
 54 int mm[maxn], best[20][maxn];
 55 void initRMQ(int n) {
 56     mm[0] = -1;
 57     for (int i = 1; i <= n; i++)
 58         mm[i] = ((i&(i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
 59     for (int i = 1; i <= n; i++)    best[0][i] = i;
 60     for (int i = 1; i <= mm[n]; i++) {
 61         for (int j = 1; j + (1 << i) - 1 <= n; j++) {
 62             int a = best[i - 1][j];
 63             int b = best[i - 1][j + (1 << (i - 1))];
 64             if (RMQ[a] < RMQ[b])    best[i][j] = a;
 65             else
 66             {
 67                 best[i][j] = b;
 68             }
 69         }
 70     }
 71 }
 72 int askRMQ(int a, int b) {
 73     int t;
 74     t = mm[b - a + 1];
 75     b -= (1 << t) - 1;
 76     a = best[t][a];    b = best[t][b];
 77     return RMQ[a] < RMQ[b] ? a : b;
 78 }
 79 int find_lcp(int a, int b) {
 80     if (a>b) swap(a, b);
 81     return lcp[askRMQ(a, b - 1)];
 82 }
 83 int main()
 84 {
 85     char inp[5];
 86     int H;
 87     scanf("%d", &H);
 88     while (H--) {
 89         scanf("%d", &len);
 90         for (int i = 0; i<len; i++) {
 91             scanf("%s", inp);
 92             if (inp[0] == 'a') S[i] = 0;
 93             else    S[i] = 1;
 94         }
 95         construct_sa();
 96         construct_lcp();
 97         for (int i = 1; i <= len; i++)    RMQ[i] = lcp[i];
 98         initRMQ(len);
 99         int ans = 1;
100         for (int i = 1; i <= len; i++) {
101             int ret = 0;
102             for (int j = 0; j + i<len; j += i) {
103                 int r1 = Rank[j], r2 = Rank[j + i];
104                 int L = find_lcp(r1, r2);
105                 int temp = L / i + 1;
106                 int k = j - (i - L%i);
107                 if (k >= 0) {
108                     temp = find_lcp(Rank[k], Rank[k + i]) / i + 1;
109                 }
110                 ret = max(ret, temp);
111             }
112             ans = max(ans, ret);
113         }
114         printf("%d\n", ans);
115     }
116     return 0;
117 }

 

转载于:https://www.cnblogs.com/Blogggggg/p/7921184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷的SPOJ需要注册一个SPOJ账号并进行绑定才能进行交题。您可以按照以下步骤进行注册: 1. 打开洛谷网站(https://www.luogu.com.cn/)并登录您的洛谷账号。 2. 在网站顶部导航栏中找到“题库”选项,将鼠标悬停在上面,然后选择“SPOJ”。 3. 在SPOJ页面上,您会看到一个提示,要求您注册SPOJ账号并进行绑定。点击提示中的链接,将会跳转到SPOJ注册页面。 4. 在SPOJ注册页面上,按照要求填写您的用户名、密码和邮箱等信息,并完成注册。 5. 注册完成后,返回洛谷网站,再次进入SPOJ页面。您会看到一个输入框,要求您输入刚刚注册的SPOJ用户名。输入用户名后,点击“绑定”按钮即可完成绑定。 现在您已经成功注册并绑定了SPOJ账号,可以开始在洛谷的SPOJ题库上刷题了。祝您顺利完成编程练习!\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [(洛谷入门系列,适合洛谷新用户)洛谷功能全解](https://blog.csdn.net/rrc12345/article/details/122500057)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [luogu p7492 序列](https://blog.csdn.net/zhu_yin233/article/details/122051384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值