hdu2328(后缀数组 + 二分)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2328

题意: 求 n 个串的字典序最小的最长公共子串

思路: 本题中单个字符串长度不超过 200, 可以暴力枚举一个字符串的所有前缀, 然后用kmp去匹配其他字符串.

我这里是用后缀数组写的. 类似 http://www.cnblogs.com/geloutingyu/p/7450580.html

不过本题是有 n 个字符串, 不能直接暴力判断. 不难想到这里可以直接二分答案长度, 不过 check 函数比较难想到, 具体看代码

代码:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #define rank Rank
  5 using namespace std;
  6
  7 const int MAXN = 1e6 + 10;
  8 int sol;
  9 char str[MAXN];
 10 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN], vis[MAXN], tag[(int)(4e3 + 10)];
 11
 12 bool cmp(int *f, int x, int y, int w){
 13     return f[x] == f[y] && f[x + w] == f[y + w];
 14 }
 15
 16 void DA(char *s, int n, int m){
 17     for(int i = 0; i < m; i++) sum[i] = 0;
 18     for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++;
 19     for(int i = 1; i < m; i++) sum[i] += sum[i - 1];
 20     for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i;
 21     for(int len = 1; len <= n; len <<= 1){
 22         int p = 0;
 23         for(int i = n - len; i < n; i++) tp[p++] = i;
 24         for(int i = 0; i < n; i++){
 25             if(SA[i] >= len) tp[p++] = SA[i] - len;
 26         }
 27         for(int i = 0; i < m; i++) sum[i] = 0;
 28         for(int i = 0; i < n; i++) sum[rank[tp[i]]]++;
 29         for(int i = 1; i < m; i++) sum[i] += sum[i - 1];
 30         for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i];
 31         swap(rank, tp);
 32         p = 1;
 33         rank[SA[0]] = 0;
 34         for(int i = 1; i < n; i++){
 35             rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++;
 36         }
 37         if(p >= n) break;
 38         m = p;
 39     }
 40     int k = 0;
 41     n--;
 42     for(int i = 0; i <= n; i++) rank[SA[i]] = i;
 43     for(int i = 0; i < n; i++){
 44         if(k) k--;
 45         int j = SA[rank[i] - 1];
 46         while(s[i + k] == s[j + k]) k++;
 47         height[rank[i]] = k;
 48     }
 49 }
 50
 51 bool check(int mid, int n, int len){
 52     int cnt = 1;
 53     memset(tag, 0, sizeof(tag));
 54     tag[vis[SA[1]]] = 1;
 55     for(int i = 2; i <= len; i++){
 56         if(height[i] >= mid){
 57             if(!tag[vis[SA[i]]]){
 58                 cnt++;
 59                 tag[vis[SA[i]]] = 1;
 60                 if(cnt >= n){
 61                     sol = SA[i];
 62                     return true;
 63                 }
 64             }
 65         }else{
 66             cnt = 1;
 67             memset(tag, 0, sizeof(tag));
 68             tag[vis[SA[i]]] = 1;
 69         }
 70     }
 71     return false;
 72 }
 73
 74 int main(void){
 75     int n;
 76     while(~scanf("%d", &n) && n){
 77         memset(vis, 0, sizeof(vis));
 78         scanf("%s", str);
 79         int len = strlen(str);
 80         int mx = len;
 81         for(int i = 1; i < n; i++){
 82             vis[len] = 1;
 83             str[len] = ‘0‘;
 84             scanf("%s", str + 1 + len);
 85             mx = max(mx, (int)(strlen(str) - len));
 86             len = strlen(str);
 87         }
 88         str[len] = 0;
 89         DA(str, len + 1, ‘z‘ + 1);
 90         for(int i = 1; i <= len; i++) vis[i] += vis[i - 1];
 91         int l = 1, r = mx;
 92         while(l <= r){
 93             int mid = (l + r) >> 1;
 94             if(check(mid, n, len)) l = mid + 1;
 95             else r = mid - 1;
 96         }
 97         if(l - 1 <= 0){
 98             puts("IDENTITY LOST");
 99             continue;
100         }
101         for(int i = sol, j = 1; j <= l - 1; i++, j++){
102             printf("%c", str[i]);
103         }
104         puts("");
105     }
106     return 0;
107 }

时间: 2025-01-04 22:12:14

hdu2328(后缀数组 + 二分)的相关文章

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; typedef long long

hdu 5008(2014 ACM/ICPC Asia Regional Xi&#39;an Online ) Boring String Problem(后缀数组&amp;二分)

Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 219    Accepted Submission(s): 45 Problem Description In this problem, you are given a string s and q queries. For each que

【bzoj4310】跳蚤 后缀数组+二分

题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究. 首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个.他称其为“魔力串”. 现在他想找一个最优的分法让“魔力串”字典序最小. 输入 第一行一个整数 k. 接下来一个长度不超过 105 的字符串 S. 输出 输出一行,表示字典序最小的“魔力串”. 样例输入 13 bcbcbacbbbbbabbacbcb

WHU---1084 - 连续技 (后缀数组+二分)

Description 不管是什么武功,多少都会有一或两个连续技多次出现,这些连续技常常是发明该武功的人的习惯性动作,如果这些动作被对手分析出来了,就很容易被对手把握住先机.比如松风剑谱里面有一式叫做迎风傲骨是如下的动作: 劈 刺 削 刺 削 踢 刺 削 刺 削 很明显 刺-削 这个连续动作出现了4次,而 刺-削-刺-削 这个连续动作则出现了两次. 现在刘白宇弄到了一本魔教的掌法,想让你帮忙来分析其中最长的且出现尽量多的连续技,当然,他不好意思麻烦你太久,只想让你告诉它这个连续技有多长并且出现了

poj 3261 Milk Patterns 后缀数组+二分

1 /*********************************************************** 2 题目: Milk Patterns(poj 3261) 3 链接: http://poj.org/problem?id=3261 4 题意: 给一串数字,求这些数字中公共子串个数大于k的 5 最长串. 6 算法: 后缀数组+二分 7 ***********************************************************/ 8 #incl

POJ1743---Musical Theme(后缀数组+二分)

Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of music

POJ 1743 Musical Theme(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=1743 [题目大意] 给出一首曲子的曲谱,上面的音符用不大于88的数字表示, 现在请你确定它主旋律的长度,主旋律指的是出现超过一次, 并且长度不小于5的最长的曲段,主旋律出现的时候并不是完全一样的, 可能经过了升调或者降调,也就是说, 是原来主旋律所包含的数字段同时加上或者减去一个数所得, 当然,两段主旋律之间也是不能有重叠的,现在请你求出这首曲子主旋律的长度, 如果不存在请输出0. [题解] 首先要处理的是升调和降调的问题,由

POJ 3080 Blue Jeans(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3080 [题目大意] 求k个串的最长公共子串,如果存在多个则输出字典序最小,如果长度小于3则判断查找失败. [题解] 将所有字符串通过拼接符拼成一个串,做一遍后缀数组,二分答案,对于二分所得值,将h数组大于这个值的相邻元素分为一组,判断组内元素是否覆盖全字典,是则答案成立,对于答案扫描sa,输出第一个扫描到的子串即可. [代码] #include <cstdio> #include <cstring> #inclu