Uva 12361 File Retrieval 后缀数组+并查集

题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合。

问最多能形成多少个不同的集合。集合不能为空。

分析:用后缀数组处理。然后首先考虑一个单词形成一个集合的情况,若该单词是其他单词的字串,则该单词显然不会形成一个集合,那么利用后缀数组,

对于每个单词看能否与其他单词有LCP,且LCP 长度为该单词本身长度。

然后就是多个单词形成集合的情况:比较简单的处理方式就是将h数组值相同的下标集中存储,比如h[x] = h[y] = h[z] = 5, 那么将x,y,z存到h

值对应为5的数组中,然后按照h值,假设为v,从大到小的顺序,将所有h值为v的下标与其周围的LCP大于v的(h[v-1],h[v])对应的子串,更新并查集。实际意义就是,每次将h值为h[v]的一些子串所在的单词合并到之前h值> h[v]的子串所在的单词形成的并查集中,得到的并查集中单词一定有长度>=h[v]公共字串,这样的并查集实际就是一个合法的单词集合,可以利用二进制表示,每次得到新的集合则将二进制表示加入到统计集合的set中,最后结果就是set的大小。

AC代码其实是比赛时写的,当时多个单词部分不是上面这种写法,不过类似。

  1 #include <bits/stdc++.h>
  2 #define  in  freopen("solve_in.txt", "r", stdin);
  3 #define  bug(x)  printf("Line %d:>>>>>>>\n", (x));
  4
  5 #define  REV(a)    reverse((a).begin(), (a).end())
  6 #define  READ(a, n) {REP(i, n) cin>>(a)[i];}
  7 #define  REP(i, n) for(int i = 0; i < (n); i++)
  8 #define  VREP(i, n, base) for(int i = (n); i >= (base); i--)
  9 #define  Rep(i, base, n) for(int i = (base); i < (n); i++)
 10 #define  REPS(s, i) for(int i = 0; (s)[i]; i++)
 11 using namespace std;
 12 typedef unsigned long long ULL;
 13 typedef long long LL;
 14 typedef map<ULL, int> UMps;
 15 set<ULL> se;
 16
 17 const int maxn = 10500 + 100;
 18 const int maxm = 66;
 19 const int maxlen = maxn*maxm+100;
 20 int s[maxlen];
 21 int sa[maxlen], t[maxlen], t2[maxlen], c[maxlen], n, m, dp[maxlen][30];
 22 int num[maxlen];
 23 LL ans;
 24 void build_sa(int m) {
 25     int *x = t, *y = t2;
 26
 27     REP(i, m) c[i] = 0;
 28     REP(i, n) c[x[i] = s[i]]++;
 29     Rep(i, 1, m) c[i] += c[i-1];
 30     VREP(i, n-1, 0) sa[--c[x[i]]] = i;
 31
 32     for(int k = 1; k <= n; k <<= 1) {
 33         int p = 0;
 34
 35         Rep(i, n-k, n) y[p++] = i;
 36         REP(i, n) if(sa[i] >= k) y[p++] = sa[i]-k;
 37
 38         REP(i, m) c[i] = 0;
 39         REP(i, n) c[x[y[i]]]++;
 40         Rep(i, 1, m) c[i] += c[i-1];
 41
 42         VREP(i, n-1, 0) sa[--c[x[y[i]]]] = y[i];
 43         swap(x, y);
 44         p = 1, x[sa[0]] = 0;
 45         Rep(i, 1, n)
 46         x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k] ? p-1 : p++;
 47         if(p >= n) break;
 48         m = p;
 49     }
 50 }
 51 int rk[maxlen], h[maxlen];
 52
 53 void getHeight() {
 54     int j, k = 0;
 55     h[0] = 0;
 56     REP(i, n) rk[sa[i]] = i;
 57     REP(i, n) {
 58         if(k) k--;
 59         if(rk[i] == 0)
 60             continue;
 61         j = sa[rk[i]-1];
 62         while( s[i+k] == s[j+k]) k++;
 63         h[rk[i]] = k;
 64     }
 65 }
 66 void RMQ_init() {
 67     REP(i, n) dp[i][0] = h[i];
 68     for(int k = 1; (1<<k) <= n; k++)
 69         for(int i = 0; i + (1<<k) <= n; i++)
 70             dp[i][k] = min(dp[i][k-1], dp[i+(1<<(k-1))][k-1]);
 71 }
 72 int RMQ(int l, int r) {
 73     int k = 0;
 74     while((1<<(k+1)) <= r-l+1) k++;
 75     return min(dp[l][k], dp[r-(1<<k)+1][k]);
 76 }
 77 char word[maxm][maxn];
 78 int nn;
 79 inline int idx(char ch) {
 80     return ch-‘a‘+1;
 81 }
 82 int vis[70], slen[70];
 83
 84 void solveSingle() {
 85     se.clear();
 86     memset(vis, 0, sizeof vis);
 87     for(int i = 1; i < n; i++){
 88         if(h[i]){
 89             if(num[sa[i]] != -1 && h[i] == slen[num[sa[i]]])
 90                 vis[num[sa[i]]] = 1;
 91             if(num[sa[i-1]] != -1 && h[i] == slen[num[sa[i-1]]])
 92                 vis[num[sa[i-1]]] = 1;
 93         }
 94     }
 95     for(int i = 0; i < nn; i++) if(!vis[i])
 96             se.insert(1ULL<<i);
 97 }
 98 void dfs(int l, int r, int now) {
 99     if(l >= r)
100         return;
101     ULL tmp;
102
103     for(int i = l; i < r; ) {
104         tmp = 0;
105         while(i < r && h[i] <= now)
106             i++;
107         if(i >= r)
108             break;
109         int mx = (int)1e9;
110         int j = i;
111         mx = min(mx, h[j]);
112         if(j < r && num[sa[j-1]] != -1)
113             tmp |= 1ULL<<num[sa[j-1]];
114         while(j < r && h[j] > now) {
115             mx = min(mx, h[j]);
116             if(num[sa[j]] != -1)
117                 tmp |= 1ULL<<num[sa[j]];
118             j++;
119         }
120         if(tmp)
121         se.insert(tmp);
122         dfs(i, j, mx);
123         i = j;
124     }
125 }
126 void solve() {
127     build_sa(120);
128     getHeight();
129     solveSingle();
130     ULL tmp;
131     for(int i = 1; i < n; ) {
132         int mx = (int)1e9;
133         tmp = 0;
134         while(i < n && !h[i])
135             i++;
136         if(i >= n)
137             break;
138         mx = min(mx, h[i]);
139         int j = i;
140         if(j < n && num[sa[j-1]] != -1)
141             tmp |= 1ULL<<num[sa[j-1]];
142         while(j < n && h[j]) {
143             mx = min(mx, h[j]);
144             if(num[sa[j]] != -1)
145                 tmp |= 1ULL<<num[sa[j]];
146             j++;
147         }
148         if(tmp)
149         se.insert(tmp);
150         dfs(i, j, mx);
151         i = j;
152     }
153     printf("%llu\n", (ULL)se.size());
154 }
155 int main() {
156
157
158     while(scanf("%d", &nn), nn) {
159         n = 0;
160         memset(num, -1, sizeof num);
161         for(int i = 0; i < nn; i++) {
162             slen[i] = 0;
163             scanf("%s", word[i]);
164             for(int j = 0; word[i][j]; j++) {
165                 slen[i]++;
166                 s[n] = idx(word[i][j]);
167                 num[n++] = i;
168             }
169             s[n++] = 30+i;
170         }
171         s[n-1] = 0;
172         solve();
173     }
174     return 0;
175 }

时间: 2024-10-12 18:11:21

Uva 12361 File Retrieval 后缀数组+并查集的相关文章

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

[UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 n杯鸡尾酒.这 n杯鸡尾酒排成一行,其中第 i杯酒 (1≤i≤n ) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一.设 Str(l,r)表示第 l杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字

【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默水过~ 本题的实现还是非常简单的,先求出height数组,然后两杯酒'r'相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了.维护连续区间可以用并查集.统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,

loj6198谢特 后缀数组+并查集+Trie

先把问题放在后缀数组上考虑 已知两个数组a b,求min(a[i],...,a[j])+(b[i]^b[j])的最大值 套路题 初始每个点都是一个小连通块 把a按从大到小的顺序加入,计算当前加入边作为min的贡献: 每次加入会把两个连通块联通,答案就是两边连通块各出一个数能得到的异或和最大值 我:这不是线性基吗 miaom:mdzz,只能有两个数 我:蛤,好难啊,怎么做啊 miaom:Trie啊 我:哦 没了 1 #include <bits/stdc++.h> 2 #define N 500

POJ 3415 Common Substrings 后缀数组+并查集

后缀数组,看到网上很多题解都是单调栈,这里提供一个不是单调栈的做法, 首先将两个串 连接起来求height   求完之后按height值从大往小合并.  height值代表的是  sa[i]和sa[i-1] 的公共前缀长度,那么每次合并就是合并  i和i-1 那么在合并小的时候公共前缀更大的肯定已经都合并在一起,那么就可以直接统计了. #include<iostream> #include<cstdio> #include<algorithm> #include<

BZOJ 4199 [Noi2015]品酒大会:后缀数组 + 并查集

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4199 题意: 给你一个长度为n的字符串s,和一个长为n的数组v. 对于每个整数r∈[0,n-1]: (1)问你有多少对后缀(suffix(i), suffix(j)),满足LCP(suffix(i), suffix(j)) >= r (2)输出mul[r] = max(v[i]*v[j]),其中i,j满足(1)的条件 题解: 先考虑第(1)问. 由于LCP只受连续的一段height中最小

bzoj 4119 后缀数组 + 并查集

并查集合并的时候更新信息.注意a[ i ] 有负的. #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> #define LL long long #define LD long double #define ull unsigned long long #define fi first #define se second

Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集

#131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 ii 杯酒 (1≤i≤n1in) 被贴上了一个标签 sisi,每个标签都是 2626 个小写英文字母之一.设 Str(l,r)Strlr 表示第 ll 杯酒到第 rr 杯酒的 

UVA 12206 - Stammering Aliens(后缀数组)

UVA 12206 - Stammering Aliens 题目链接 题意:给定一个序列,求出出现次数大于m,长度最长的子串的最大下标 思路:后缀数组,搞出height数组后,利用二分去查找即可 这题之前还写过hash的写法也能过,不过写后缀数组的时候,犯了一个傻逼错误,把none输出成node还一直找不到...这是刷题来第二次碰到这种逗比错误了,还是得注意.. 代码: #include <cstdio> #include <cstring> #include <algori