poj 1743 后缀数组

 

思路:要求A中两子串之差为定值,将每相邻两个数相减所得数列B,等价于要求B中子串相等。从而转换LCP(longest commen prefix)问题,又要求不能重叠,采用二分答案方法求解。要注意的是二分期间的判断search_ans:若果h[i]>=len,并且h[j]>=len,则sa[i]..s[j]中的最大与最小值之差即为重叠瓶颈!


 

  1. #include <iostream>
  2. #include <string>
  3. #include <algorithm>
  4. using namespace std;
  5. #define Min(a,b) (a)<(b)?(a):(b)
  6. #define Max(a,b) (a)>(b)?(a):(b)
  7. const int N = 41000;
  8. const int D = 10;
  9. const int M = 200, offset = 100;
  10. int n;
  11. int s[N];
  12. int cnt[N], mem[4][N], *rank, *nrank, *sa, *nsa, h[N];
  13. // lcp[i][j]: longest commen prefix ( suffix(sa[k+1]), suffix(sa[k]) ) j <= k < j+2^i
  14. void radix_sort()
  15. {
  16.  int i, j, k;
  17.  rank = mem[0];
  18.  nrank = mem[1];
  19.  sa = mem[2];
  20.  nsa = mem[3];
  21.  for(i = 0; i < n; i++) cnt[s[i]]++;
  22.  for(i = 1; i < M; i++) cnt[i] += cnt[i-1];
  23.  for(i = n-1; i >= 0; i--) sa[--cnt[s[i]]] = i;
  24.  for(rank[0]=0, i=1; i < n; i++)
  25.  {
  26.   rank[sa[i]] = rank[sa[i-1]];
  27.   if(s[sa[i]]!=s[sa[i-1]]) rank[sa[i]]++;
  28.  }
  29.  for(k = 1; k<n && rank[sa[n-1]] < n-1; k*=2)
  30.  {
  31.   for(i = 0; i < n; i++) cnt[rank[sa[i]]] = i+1;
  32.   for(i = n-1; i >= 0; i--) if(sa[i]-k>=0)
  33.    nsa[--cnt[rank[sa[i]-k]]] = sa[i]-k;
  34.   // max(sa[i]-k)=n-k-1 , therefore i = n-k;
  35.   for(i = n-k; i < n; i++)
  36.    nsa[--cnt[rank[i]]] = i;
  37.   for(nrank[nsa[0]], i=1; i < n; i++)
  38.   {
  39.    nrank[nsa[i]] = nrank[nsa[i-1]];
  40.    if(rank[nsa[i]] != rank[nsa[i-1]]
  41.     || rank[nsa[i]+k] != rank[nsa[i-1]+k])
  42.    nrank[nsa[i]]++;
  43.   }
  44.   swap(rank, nrank);
  45.   swap(sa, nsa);
  46.  }
  47. }
  48. void get_lcp_rmq()
  49. {
  50.  int i, j, k;
  51.  for(i=0,k=0; i<n; i++)
  52.  {
  53.   if(rank[i]==n-1) h[rank[i]]=k=0;
  54.   else
  55.   {
  56.    if(k>0)k--;
  57.    j = sa[rank[i]+1];
  58.    for(;s[i+k]==s[j+k];k++) ;
  59.    h[rank[i]]=k;
  60.   }
  61.  }
  62. }
  63. /*
  64. 9
  65. 1 2 3 4 5 6 7 8 9
  66. */
  67. bool search_ans(int len)
  68. {
  69.  int i, l=N, r=0, ma, mi;
  70.  for(i = 0; i < n-2; i++)
  71.  {
  72.   if( h[i] < len) l=N,r=0;
  73.   else
  74.   {
  75.    mi = sa[i];
  76.    ma = sa[i+1];
  77.    if(mi > ma) swap(mi,ma);
  78.    l = Min(mi, l);
  79.    r = Max(ma, r);
  80.    if(r - l >= len)return true;
  81.   }
  82.  }
  83.  return false;
  84. }
  85. int main()
  86. {
  87.  int i, j, k;
  88.  while(scanf("%d", &n)==1 && n)
  89.  {
  90.   memset(cnt, 0, sizeof(cnt));
  91.   memset(mem, 0, sizeof(mem));
  92.   for(i=0;i<n;i++) scanf("%d",&s[i]);
  93.   for(i=0;i<n-1;i++) s[i] = s[i+1]-s[i]+offset;
  94.   s[n-1]=0;
  95.   //for(i=0;i<n;i++)printf("%d ",s[i]); puts("");
  96.   radix_sort();
  97.   get_lcp_rmq();
  98.   int low = 0, up = n, mid;
  99.   bool get=0;
  100.   while(low < up)
  101.   {
  102.    mid = (low+up+1)/2;
  103.    if( get=search_ans(mid) ) low = mid;
  104.    else up = mid-1;
  105.   }
  106.   if(low < 4)puts("0");
  107.   else printf("%d/n", low+1);
  108.  }
  109.     return 0;
  110. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值