CSU1632Repeated Substrings(后缀数组/最长公共前缀)

题意就是求一个字符串的重复出现(出现次数>=2)的不同子串的个数。

标准解法是后缀数组、最长公共前缀的应用,对于样例aabaab,先将所有后缀排序:

aab

3    aabaab

1    ab

2    abaab

0    b

1    baab

每个后缀前面数字代表这个后缀与它之前的后缀(rank比它小1)的最长公共前缀的长度:然而就可以这样理解这个最长公共前缀LCP、aabaab与aab的最长公共前缀是3,那说明子串a、aa、aab都至少出现的两次,那么这就是后缀aab重复出现的子串个数

然后我们考虑后缀ab与后缀aabaab的最长公共前缀=1,这时由于LCP(ab, aabaab) <= LCP(aabaab, aab),所以就说明ab与aabaab的所有公共前缀(只有LCP(ab, aabaab) = 1个)之前都已经计算过了,所以我们直接跳过

之后我们考虑后缀abaab与后缀ab的最长公共前缀LCP(abaab, ab) > LCP(ab, aabaab),同时,由于LCP(ab, aabaab) != 0,所以考虑abaab与ab的两个公共前缀(重复出现的子串)a、ab时,已经有了LCP(ab, aabaab) = 1个公共前缀(重复出现的子串)a已经计算过了,所以这时新的重复出现的子串的个数为LCP(abaab, ab) - LCP(ab, aabaab) = 1

最后总结起来就是:

    if(height[i] <= height[i - 1]) continue;

    if(height[i] > height[i - 1] ) ans += height[i] - height[i - 1]

这就是最后的答案。

  1 #include <map>
  2 #include <set>
  3 #include <stack>
  4 #include <queue>
  5 #include <cmath>
  6 #include <ctime>
  7 #include <vector>
  8 #include <cstdio>
  9 #include <cctype>
 10 #include <cstring>
 11 #include <cstdlib>
 12 #include <iostream>
 13 #include <algorithm>
 14 using namespace std;
 15 #define INF 0x3f3f3f3f
 16 #define inf (-((LL)1<<40))
 17 #define lson k<<1, L, (L + R)>>1
 18 #define rson k<<1|1,  ((L + R)>>1) + 1, R
 19 #define mem0(a) memset(a,0,sizeof(a))
 20 #define mem1(a) memset(a,-1,sizeof(a))
 21 #define mem(a, b) memset(a, b, sizeof(a))
 22 #define FIN freopen("in.txt", "r", stdin)
 23 #define FOUT freopen("out.txt", "w", stdout)
 24 #define rep(i, a, b) for(int i = a; i <= b; i ++)
 25 #define dec(i, a, b) for(int i = a; i >= b; i --)
 26
 27 template<class T> T CMP_MIN(T a, T b) { return a < b; }
 28 template<class T> T CMP_MAX(T a, T b) { return a > b; }
 29 template<class T> T MAX(T a, T b) { return a > b ? a : b; }
 30 template<class T> T MIN(T a, T b) { return a < b ? a : b; }
 31 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
 32 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }
 33
 34 //typedef __int64 LL;
 35 typedef long long LL;
 36 const int MAXN = 110000;
 37 const int MAXM = 110000;
 38 const double eps = 1e-4;
 39 LL MOD = 1000000007;
 40
 41 struct SufArray {
 42     char s[MAXN];
 43     int sa[MAXN], t[MAXN], t2[MAXN], c[MAXN], n, m;
 44     int rnk[MAXN], height[MAXN];
 45     int mi[MAXN][20], idxK[MAXN];
 46
 47     void init() {
 48         mem0(s);
 49         mem0(height);
 50     }
 51     void read_str() {
 52         gets(s);
 53         m = 128;
 54         n = strlen(s);
 55         s[n++] = ‘ ‘;
 56     }
 57     void build_sa() {
 58         int *x = t, *y = t2;
 59         rep (i, 0, m - 1) c[i] = 0;
 60         rep (i, 0, n - 1) c[x[i] = s[i]] ++;
 61         rep (i, 1, m - 1) c[i] += c[i - 1];
 62         dec (i, n - 1, 0) sa[--c[x[i]]] = i;
 63         for(int k = 1; k <= n; k <<= 1) {
 64             int p = 0;
 65             rep (i, n - k, n - 1) y[p++] = i;
 66             rep (i, 0, n - 1) if(sa[i] >= k) y[p++] = sa[i] - k;
 67             rep (i, 0, m - 1) c[i] = 0;
 68             rep (i, 0, n - 1) c[x[y[i]]] ++;
 69             rep (i, 0, m - 1) c[i] += c[i - 1];
 70             dec (i, n - 1, 0) sa[--c[x[y[i]]]] = y[i];
 71             swap(x, y);
 72             p = 1;
 73             x[sa[0]] = 0;
 74             rep (i, 1, n - 1) {
 75                 x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++;
 76             }
 77             if(p >= n) break;
 78             m = p;
 79         }
 80     }
 81     void get_height() {
 82         int k = 0;
 83         rep (i, 0, n - 1) rnk[sa[i]] = i;
 84         rep (i, 0, n - 1) {
 85             if(k) k --;
 86             int j = sa[rnk[i] - 1];
 87             while(s[i + k] == s[j + k]) k ++;
 88             height[rnk[i]] = k;
 89         }
 90     }
 91     void rmq_init(int *a, int n) {
 92         rep (i, 0, n - 1) mi[i][0] = a[i];
 93         for(int j = 1; (1 << j) <= n; j ++) {
 94             for(int i = 0; i + (1<<j) - 1 < n; i ++) {
 95                 mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
 96             }
 97         }
 98         rep (len, 1, n) {
 99             idxK[len] = 0;
100             while((1 << (idxK[len] + 1)) <= len) idxK[len] ++;
101         }
102     }
103     int rmq_min(int l, int r) {
104         int len = r - l + 1, k = idxK[len];
105         return min(mi[l][k], mi[r - (1 << k) + 1][k]);
106     }
107     void lcp_init() {
108         get_height();
109         rmq_init(height, n);
110     }
111     int get_lcp(int a, int b) {
112         if(a == b) return n - a - 1;
113         return rmq_min(min(rnk[a], rnk[b]) + 1, max(rnk[a], rnk[b]));
114     }
115     void solve() {
116         get_height();
117         LL ans = 0, pre = 0;
118         rep (i, 1, n - 1) {
119             if(height[i] > pre) ans += height[i] - pre;
120             pre = height[i];
121         }
122         cout << ans << endl;
123     }
124 };
125
126 int T;
127 SufArray sa;
128
129 int main()
130 {
131     while(~scanf("%d%*c", &T)) while(T--){
132         sa.init();
133         sa.read_str();
134         sa.build_sa();
135         sa.solve();
136     }
137     return 0;
138 }
139 /**************************************************************
140     Problem: 1632
141     User: csust_Rush
142     Language: C++
143     Result: Accepted
144     Time:880 ms
145     Memory:13192 kb
146 ****************************************************************/
时间: 2024-11-12 09:34:23

CSU1632Repeated Substrings(后缀数组/最长公共前缀)的相关文章

[poj 2274]后缀数组+最长公共子序列

题目链接:http://poj.org/problem?id=2774 后缀数组真的太强大了,原本dp是n^2的复杂度,在这里只需要O(n+m). 做法:将两个串中间夹一个未出现过的字符接起来,然后做一次后缀数组,得到的height相邻两个排名的后缀,在串中的位置如果满足在分界符左右两侧,就更新最长公共前缀.最后得到的最大值就是最长公共子序列. #include<algorithm> #include<cstdio> #include<cstring> using na

POJ 2217 (后缀数组+最长公共子串)

题目链接: http://poj.org/problem?id=2217 题目大意: 求两个串的最长公共子串,注意子串是连续的,而子序列可以不连续. 解题思路: 有个炒鸡快的O(n)的Manacher算法.不过只能求裸的最长公共和回文子串. 后缀数组解法是这类问题的模板解法. 对于n个串的最长公共子串,这要把这些串连在一起,中间用"$"这类的特殊符号分隔一下. 先求后缀数组,再求最长公共前缀,取相邻两个且属于不同串的sa的最大LCP即可. 原理就是:这样把分属两个串的LCP都跑了一遍,

POJ 2774 (后缀数组 最长公共字串) Long Long Message

用一个特殊字符将两个字符串连接起来,然后找最大的height,而且要求这两个相邻的后缀的第一个字符不能在同一个字符串中. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 200000 + 10; 7 8 char s[maxn]; 9 int n; 10 int sa[maxn], rank[maxn],

求字符串数组最长公共前缀

public class LongestCommonPrefix { public String longestCommonPrefix(String[] strs) { if(strs == null) return null; if(strs.length == 0) return ""; String s = strs[0]; for(int i = 0; i < s.length(); i ++) { for(int j = 1; j < strs.length;

SPOJ694&amp;&amp;SPOJ705:Distinct Substrings(后缀数组)

Description Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output one number saying the numb

poj Common Substrings(后缀数组&amp;单调队列)

Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7082   Accepted: 2355 Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S, a

POJ1226:Substrings(后缀数组)

Description You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings. Input The first line of the input contains a

SPOJ 694 || 705 Distinct Substrings ( 后缀数组 &amp;&amp; 不同子串的个数 )

题意 : 对于给出的串,输出其不同长度的子串的种类数 分析 : 有一个事实就是每一个子串必定是某一个后缀的前缀,换句话说就是每一个后缀的的每一个前缀都代表着一个子串,那么如何在这么多子串or后缀的前缀中找出不同的并计数呢?思路就是所有的可能子串数 - 重复的子串数.首先我们容易得到一个长度为 len 的串的子串数为 len * ( len + 1) / 2.那如何知道重复的子串数呢?答案就是利用后缀数组去跑一遍 Height ,得到所有的最长公共前缀(LCP),这些最长公共前缀的值都存在了 He

POJ3415:Common Substrings(后缀数组+单调栈)

Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤i+k-1≤|T|. Given two strings A, B and one integer K, we define S, a set of triples (i, j, k): S = {(i, j, k) | k≥K, A(i, k)=B(j, k)}. You are to give the value of |S| f