POJ 1743 (后缀数组)

来源:互联网 发布:国产复合弓 知乎 编辑:程序博客网 时间:2024/06/07 23:10

思路:
首先构建后缀数组,这里要注意theme可以相差k,如abcdefgh中,d-a == e-b因此可以将原数组相邻元素做差,s[i] = s[i] - s[i+1],这样就转化为大小为n-1的数组建立后缀树组。
然后利用二分法搜索可能的长度,判断该长度是不是无重叠重复子串的长度。对于每一个长度d,将height中大于等于d的保留,小于的去除,这样就将height分为几组,因为求最大重复子串就是求某个区间中最小的height,因此最长的无重叠子串一定在同一组内,判断是否重叠就是,利用sa数组看它们在实际数组中的位置间隔是否比d大,如果大则不重叠。
后缀数组是套用的吉林大学的模板(网上大多数的解题报告都是套的国家集训队的模板),真心给这些大牛跪了,感觉比国家集训队论文给的代码效率要高,并且简洁到令人发指的地步,只有望其项背的份了,不过简洁的代码太尼玛难懂了,看了整整一天才明白啊有木有!!

#include <iostream>using namespace std;const int N = 20050;int s[N];                         //store numbersint n, sa[N], height[N], rank[N], sum[N], tsa[N];inline int min(int a, int b){return a < b ? a : b;}inline int max(int a, int b){return a > b ? a : b;}void makesa(int na){                       // O(N * log N) int i, j, len; //n = strlen(s) + 1;//na = (n < 256 ? 256 : n); memset(sum, 0, na * sizeof(int)); for (i = 0; i < n ; i++) sum[ rank[i] = s[i]]++; for (i = 1; i < na; i++) sum[i] += sum[i - 1]; for (i = 0; i < n; i++) sa[ --sum[ rank[i] ] ] = i; //第一关键字排序 for (len = 1; len < n; len <<= 1) { //第二关键字排序:首先将sa看做第二关键字,j就是第一关键字,将第一关键字按照第二关键字//出现的顺序放入到tsa中,也就完成了双关键字的排序for (i = 0; i < n; i++) { j = sa[i] - len; if (j < 0) j += n; tsa[ sum[ rank[j] ]++ ] = j; } sa[ tsa[ sum[0] = 0 ] ] = j = 0; //j代表排名for (i = 1; i < n; i++) {//如果当前项的两个关键字中,第一个和第二个都和排在前面项的相同,则代表//该项和前一项排名相同,j不变if (rank[ tsa[i] ] != rank[ tsa[i-1] ] || rank[ tsa[i]+len ]!=rank[ tsa[i-1]+len ]) sum[++j] = i; //这里sum记录了相同项中,第一次出现的项的排名,也就是相同项中排名最高者 sa[ tsa[i] ] = j; //这里将rank值放入sa,sa这里只是作为一个缓冲} memcpy(rank, sa , n * sizeof(int)); memcpy(sa  , tsa, n * sizeof(int)); if (j >= n - 1) break; } }void lcp(){                          // O(4 * N) int i, j, k; j = rank[0];height[0] = 0;for (i = k = 0; i < n - 1; i++, k++) while (k >= 0 && s[i] != s[ sa[j-1] + k ]) height[j] = (k--), j = rank[ sa[j] + 1 ]; }/*利用二分法搜索可能的长度,然后判断是不是无重叠重复子串的长度。对于每一个长度d,将height中大于等于d的保留,小于的去除,这样就将height非为几组,因为求最大重复子串就是求某个区间中最小的height,因此最长的无重叠子串一定在同一组内,判断是否重叠就是,利用sa数组看它们在实际数组中的位置间隔是否比d大,如果da则不重叠。*/bool check(int mid){int low = sa[1], high = sa[1];for (int i = 2; i < n; ++i){if (height[i] >= mid){low = min(low, sa[i]);high = max(high, sa[i]);if (high - low >= mid) return true;}else{low = sa[i];high = sa[i];}}return false;}void solve(){int l = 1, r = n, mid, i, ans = 0;while (l < r){mid = (l + r) / 2;if (check(mid)) ans = mid, l = mid + 1;else r = mid;}if (ans >= 4) printf("%d\n", ans+1);else printf("0\n");}int main(){int i;while (scanf("%d", &n) && n){for (i = 0; i < n; ++i)scanf("%d", &s[i]);for (i = 0; i < n-1; ++i)s[i] = s[i] - s[i+1] + 100;s[i] = 0;makesa(200);lcp();solve();}return 0;}


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 雅思成绩丢了怎么办 上网要求刷脸怎么办 高考选修考d怎么办 考研两门专业课怎么办 考研数学太难怎么办 考研英语不过线怎么办 英语差考研复试怎么办 高中被学校开除怎么办 bois密码忘记了怎么办 xp系统管理员账号停用怎么办 学业负担太重怎么办 小学生手册丢了怎么办 自己不会写论文怎么办 写论文很心慌怎么办 写论文没有词怎么办 亲人入了邪教怎么办 小孩脾气很倔怎么办 研究生读不下去怎么办 国外打工生活费不够怎么办 本科学位被追回怎么办 本科学位丢了怎么办 国外留学挂科怎么办 英国预科被劝退怎么办? 如果预科没合格怎么办 研究生学费太贵怎么办 澳洲移民配额用不完怎么办 去美国探亲怎么办签证 去伊朗的签证怎么办 房产证被偷了怎么办 打黑工被发现怎么办 几年前被犬咬没打针怎么办 对狂犬疫苗过敏怎么办 想去墨尔本留学怎么办 大学没交学费会怎么办 交学费收据丢了怎么办 上海浦东金科苑幼儿园统筹怎么办 红酒瓶塞掉了怎么办 红酒瓶塞丢了怎么办 收入证明没有公章怎么办 父母没工作怎么办签证 在温哥华怎么办新西兰签证