【后缀数组之应用】【待续】

【最长重复子串问题】

可重叠最长重复子串 -- POJ 3261

题意:给出包含n个元素的数组a,问其中最长可重叠重复子串的长度,要求该子串至少重复k次;输入保证至少存在一个重复k次的最长子串;

难点:二分查找子串的长度,注意对该长度的子串是否存在K个重复子串的判定方法;

代码:

  1 /*
  2 Problem: POJ 3261
  3 Tips: 可重复最长重复子串,后缀数组,二分
  4 Time: 172ms
  5 Date: 2015.8.31
  6 */
  7 #include <iostream>
  8 #include <cstring>
  9 #include <cstddef>
 10 #include <cstdio>
 11 #include <string>
 12 #include <algorithm>
 13 const int maxn = 100005;
 14 const int maxm = 1000005;
 15 int wa[maxm],wb[maxn],wv[maxn],ws[maxm];
 16 int sa[maxn], rank[maxn],height[maxn];
 17 int cmp(int *rank, int a,int b,int l)
 18 {
 19     return rank[a]==rank[b] && rank[a+l]==rank[b+l];
 20 }
 21 void debug1(int* r, int n)
 22 {
 23     for(int i = 0; i < n; i++)
 24     {
 25         printf("sa[%d]: %d\n", i, sa[i]);
 26         int p = sa[i];
 27         for(; p < n; p++)
 28             printf("%d ", r[p]);
 29         printf("\n");
 30     }
 31 }
 32 void debug2(int* r, int n)
 33 {
 34     for(int i = 0; i < n; i++)
 35     {
 36         printf("height[rank[%d]]: %d\n", i, height[rank[i]]);
 37     }
 38 }
 39 void da(int *r,int n,int m)
 40 {
 41     int i, k, p, *x=wa, *y=wb, *t;
 42     for(i=0;i<m;i++) ws[i] = 0;
 43     for(i=0;i<n;i++) ws[x[i] = r[i]]++;
 44     for(i=1;i<m;i++) ws[i] += ws[i-1];
 45     for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i;
 46     for(k=1, p=1; p<n; k*=2, m=p)
 47     {
 48         for(p=0,i=n-k;i<n;i++) y[p++]=i;
 49         for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
 50         for(i=0;i<n;i++) wv[i]=x[y[i]];
 51         for(i=0;i<m;i++) ws[i]=0;
 52         for(i=0;i<n;i++) ws[wv[i]]++;
 53         for(i=1;i<m;i++) ws[i]+=ws[i-1];
 54         for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
 55         t=x,x=y,y=t;
 56         for(p=1,x[sa[0]]=0,i=1;i<n;i++)
 57             x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
 58     }
 59     //debug1(r, n);
 60     return;
 61 }
 62 void calheight(int *r,int n)
 63 {
 64     int i,j,k=0;
 65     for(i=1;i<=n;i++) rank[sa[i]]=i;
 66     for(i=0;i<n;height[rank[i++]]=k)
 67         for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++)
 68             ;
 69     //debug2(r, n);
 70     return;
 71 }
 72 bool check(int mid, int n, int k) //Notice!
 73 {
 74     int i = 1; //height[0] = 0;
 75     while(1)
 76     {
 77         while(i <= n && height[i] < mid)    i++;
 78         if(i == n+1) break;
 79         int cnt = 1;
 80         while(i <= n && height[i] >= mid)   i++, cnt++;
 81         if(cnt >= k) return true;
 82     }
 83     return false;
 84 }
 85 int bin_search(int n, int k)
 86 {
 87     int l = 1, r = n, mid;
 88     int ans = 0;
 89     while(l <= r)
 90     {
 91         mid = l+(r-l)/2;
 92         if(check(mid, n, k))
 93             ans = mid, l = mid+1;
 94         else
 95             r = mid-1;
 96     }
 97     return ans;
 98 }
 99 int main()
100 {
101     int n, k, r[maxn];
102     scanf("%d%d", &n, &k);
103     for(int i = 0; i < n; i++) {scanf("%d", &r[i]); r[i]++;}
104     r[n] = 0;
105     da(r,n+1,maxm);
106     calheight(r,n);
107     int ans = bin_search(n, k);
108     printf("%d\n", ans);
109     return 0;
110 }

时间: 2024-10-12 19:22:11

【后缀数组之应用】【待续】的相关文章

【poj3693】Maximum repetition substring(后缀数组+RMQ)

自己看着大牛的论文学了一下后缀数组,看了好久好久,想了好久好久才懂了一点点皮毛TAT 然后就去刷传说中的后缀数组神题,poj3693是进化版的,需要那个相同情况下字典序最小,搞这个搞了超久的说. 先简单说一下后缀数组.首先有几个重要的数组: ·SA数组(后缀数组):保存所有后缀排序后从小到大的序列.[即SA[i]=j表示排名第i的后缀编号为j]        ·rank数组(名次数组):记录后缀的名次.[即rank[i]=j表示编号为i的后缀排名第j] 用倍增算法可以在O(nlogn)时间内得出

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

【tyvj1860】后缀数组

描述 我们定义一个字符串的后缀suffix(i)表示从s[i]到s[length(s)]这段子串.后缀数组(Suffix array)SA[i]中存放着一个排列,满足suffix(sa[i])<suffix(sa[i+1]) 按照字典序方式比较定义height[i]表示suffix(sa[i])与suffix(sa[i-1])之间的最长公共前缀长度,其中height[1]=0你的任务就是求出SA和height这两个数组.字符串长度<=200000 输入格式 一行,为描述中的字符串(仅会出现小写

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

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

hdu 4416 Good Article Good sentence(后缀数组&amp;思维)

Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2308    Accepted Submission(s): 649 Problem Description In middle school, teachers used to encourage us to pick up pre

uva 10829 - L-Gap Substrings(后缀数组)

题目链接:uva 10829 - L-Gap Substrings 题目大意:给定一个字符串,问有多少字符串满足UVU的形式,要求U非空,V的长度为g. 解题思路:对字符串的正序和逆序构建后缀数组,然后枚举U的长度l,每次以长度l分区间,在l和l+d+g所在的两个区间上确定U的最大长度. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using nam