[TyvjP1515] 子串统计 [luoguP2408] 不同子串个数(后缀数组)

Tyvj传送门

luogu传送门

 

经典题

统计一个字符串中不同子串的个数

一个字符串中的所有子串就是所有后缀的前缀

先求出后缀数组,求出后缀数组中相邻两后缀的 lcp

那么按照后缀数组中的顺序遍历求解

每一个后缀 suffix(sa[i]) 对于答案的贡献为 len - sa[i] - height[i]

len - sa[i] 为当前后缀的长度,也就是当前后缀所有前缀的个数(字符串从 0 开始)

height[i] 就是相邻两后缀 lcp,因为有可能会有相同前缀,而相同前缀在前面已经计算过了

为什么只需要 height 数组,而不用把任意两后缀的 lcp 求出来呢?

因为所有后缀已经按照字典序排序了,也就是说,sa[i] 和 sa[i - 1] 的 lcp 即为 sa[i] 和 sa[0 ~ i - 1] 的所有 lcp 的最大值。

 

——代码(Tyvj)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #define N 200001
 5 #define LL long long
 6 
 7 LL ans;
 8 int len, m = 256;
 9 int buc[N], x[N], y[N], sa[N], rank[N], height[N];
10 char s[N];
11 
12 inline void build_sa()
13 {
14     int i, k, p;
15     for(i = 0; i < m; i++) buc[i] = 0;
16     for(i = 0; i < len; i++) buc[x[i] = s[i]]++;
17     for(i = 1; i < m; i++) buc[i] += buc[i - 1];
18     for(i = len - 1; i >= 0; i--) sa[--buc[x[i]]] = i;
19     for(k = 1; k <= len; k <<= 1)
20     {
21         p = 0;
22         for(i = len - 1; i >= len - k; i--) y[p++] = i;
23         for(i = 0; i < len; i++) if(sa[i] >= k) y[p++] = sa[i] - k;
24         for(i = 0; i < m; i++) buc[i] = 0;
25         for(i = 0; i < len; i++) buc[x[y[i]]]++;
26         for(i = 1; i < m; i++) buc[i] += buc[i - 1];
27         for(i = len - 1; i >= 0; i--) sa[--buc[x[y[i]]]] = y[i];
28         std::swap(x, y);
29         p = 1, x[sa[0]] = 0;
30         for(i = 1; i < len; i++)
31             x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
32         if(p >= len) break;
33         m = p;
34     }
35 }
36 
37 inline void build_height()
38 {
39     int i, j, k = 0;
40     for(i = 0; i < len; i++) rank[sa[i]] = i;
41     for(i = 0; i < len; i++)
42     {
43         if(!rank[i]) continue;
44         if(k) k--;
45         j = sa[rank[i] - 1];
46         while(s[i + k] == s[j + k] && i + k < len && j + k < len) k++;
47         height[rank[i]] = k;
48     }
49 }
50 
51 int main()
52 {
53     int i;
54     scanf("%d", &len);
55     getchar();
56     for(i = 0; i < len; i++)
57     {
58         s[i] = getchar();
59         if((i + 1) % 80 == 0) getchar();
60     }
61     build_sa();
62     build_height();
63     for(i = 0; i < len; i++) ans += (LL)(len - sa[i] - height[i]);
64     printf("%lld\n", ans);
65     return 0;
66 }
View Code

 

洛谷那题好像数据有点问题。

转载于:https://www.cnblogs.com/zhenghaotian/p/6991184.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值