POJ 2758 后缀数组

 思路:

1.如果没有操作I,也就是insert操作,则很容易想到用后缀数组处理,直接计算lcp(i,j)即可,要注意的是用rmq计算lcp[i][j]的时候,如果i==j,要做特殊处理!

2.题目有个很容易误解的地方,就是Q操作的时候是对原始下标,而I操作的p是插入后的下标

3.计算Q(i,j)的时候,首先计算lcp(i,j),

    1)如果lcp(i,j)大于离i,j最近的插入字符到i,j的距len, 则定位到i+len, j+len, 比较i+len,j+len处的插入字符,从而问题转换为lcp(i',j'),重复即可。

    2)如果lcp(i,j)小于等于len,则答案即为lcp(i,j);

 

 

 

 

  1. #include <iostream>
  2. #include <string>
  3. #include <algorithm>
  4. using namespace std;
  5. #define Min(a,b) (a)<(b)?(a):(b)
  6. const int N = 51000;
  7. const int M = 260;
  8. int n, m;
  9. char s[N];
  10. struct
  11. {
  12.     char c;
  13.     int p;
  14. }si[M];
  15. int cnt[N], mem[4][N], *rank, *nrank, *sa, *nsa, lcp[17][N];
  16. // lcp[i][j]: longest commen prefix ( suffix(sa[k+1]), suffix(sa[k]) ) j <= k < j+2^i
  17. void radix_sort()
  18. {
  19.     int i, j, k;
  20.     rank = mem[0];
  21.     nrank = mem[1];
  22.     sa = mem[2];
  23.     nsa = mem[3];
  24.     for(i = 0; i < n; i++) cnt[s[i]]++;
  25.     for(i = 1; i < M; i++) cnt[i] += cnt[i-1];
  26.     for(i = n-1; i >= 0; i--) sa[--cnt[s[i]]] = i;
  27.     for(rank[0]=0, i=1; i < n; i++)
  28.     {
  29.         rank[sa[i]] = rank[sa[i-1]];
  30.         if(s[sa[i]]!=s[sa[i-1]]) rank[sa[i]]++;
  31.     }
  32.     for(k = 1; k<n && rank[sa[n-1]] < n-1; k*=2)
  33.     {
  34.         for(i = 0; i < n; i++) cnt[rank[sa[i]]] = i+1;
  35.         for(i = n-1; i >= 0; i--) if(sa[i]-k>=0)
  36.             nsa[--cnt[rank[sa[i]-k]]] = sa[i]-k;
  37.         // max(sa[i]-k)=n-k-1 , therefore i = n-k;
  38.         for(i = n-k; i < n; i++)
  39.             nsa[--cnt[rank[i]]] = i;
  40.         for(nrank[nsa[0]], i=1; i < n; i++)
  41.         {
  42.             nrank[nsa[i]] = nrank[nsa[i-1]];
  43.             if(rank[nsa[i]] != rank[nsa[i-1]]
  44.                 || rank[nsa[i]+k] != rank[nsa[i-1]+k])
  45.             nrank[nsa[i]]++;
  46.         }
  47.         swap(rank, nrank);
  48.         swap(sa, nsa);
  49.     }
  50. }
  51. void print()
  52. {
  53.     for(int i=0;i<n;i++) printf("%d %s/n", sa[i], s + sa[i]);puts("");
  54.     puts(s);for(int i=0;i<n;i++) printf("%d ", rank[i]);puts("");
  55. }
  56. void get_lcp_rmq()
  57. {
  58.     int i, j, k;
  59.     for(i=0,k=0; i<n; i++)
  60.     {
  61.         if(rank[i]==n-1) lcp[0][rank[i]]=k=0;
  62.         else
  63.         {
  64.             if(k>0)k--;
  65.             j = sa[rank[i]+1];
  66.             for(;s[i+k]==s[j+k];k++) ;
  67.             lcp[0][rank[i]]=k;
  68.         }
  69.     }
  70.     for(i=0,k=1; k<n; k*=2, i++)
  71.     {
  72.         for(j = 0; j+k < n; j++)
  73.             lcp[i+1][j] = Min(lcp[i][j], lcp[i][j+k]);
  74.     }
  75. }
  76. int rmq(int a, int b)
  77. {
  78.     int i, j, k;
  79.     a = rank[a];
  80.     b = rank[b];
  81.     if(a>b) swap(a,b);
  82.     int t=b-a;
  83.     for(i=0,k=1; 2*k<t; i++,k*=2) ;
  84.     return Min(lcp[i][a], lcp[i][b-k]); // not  b-k+1
  85.     // lcp[0][i]: LCP(i,i+1)
  86. }
  87. /*
  88. aaaaaaaa
  89. I a 2
  90. I b 3
  91. Q 1 3
  92. */
  93. int query(int a, int b)
  94. {
  95.     int i,j,k;
  96.     int mi, ret=0;
  97.     int x, y, t;
  98.     char c1, c2;
  99.     for(x = 0; si[x].p <= a; x++);
  100.     for(y = 0; si[y].p <= b; y++);
  101.     if(a==b) return n-a+m-x-2;
  102.     while(1)
  103.     {
  104.         k = rmq(a, b);
  105.         i = si[x].p-a;
  106.         j = si[y].p-b;
  107.         t = Min(k, Min(i,j));
  108.         ret+=t; a+=t; b+=t;
  109.         if(t==i || t==j)
  110.         {
  111.             while(si[x].p==a&&si[y].p==b)
  112.             {
  113.                 if(si[x].c==si[y].c) x++,y++,ret++;
  114.                 else return ret;
  115.             }
  116.             while(si[x].p==a)
  117.             {
  118.                 if(si[x].c==s[b]) x++,b++,ret++;
  119.                 else return ret;
  120.             }
  121.             while(si[y].p==b)
  122.             {
  123.                 if(si[y].c==s[a]) y++,a++,ret++;
  124.                 else return ret;
  125.             }
  126.         }
  127.         else return ret;
  128.     }
  129.     return ret;
  130. }
  131. void insert(char ch, int pos)
  132. {
  133.     int i;
  134.     for(i = 0; i < m; i++)
  135.     {
  136.         if(si[i].p>=pos)break;
  137.         else pos--;
  138.     }
  139.     if(pos >= n) pos = n-1;
  140.     int j=i;
  141.     for(i = m++; i > j; i--) si[i] = si[i-1];
  142.     si[j].c = ch;
  143.     si[j].p = pos;
  144. }
  145. int main()
  146. {
  147.     int i, j, k;
  148.     char cmd[10];
  149.     scanf("%s", s);
  150.     n = strlen(s);
  151.     s[n++]=0;
  152.     radix_sort();
  153.     get_lcp_rmq();
  154.     si[0].c = 0;
  155.     si[0].p = N;
  156.     m = 1;
  157.     scanf("%d", &k);
  158.     while(k--)
  159.     {
  160.         scanf("%s", cmd);
  161.         if(cmd[0]=='Q')
  162.         {
  163.             scanf("%d%d", &i, &j);
  164.             printf("%d/n", query(i-1,j-1));
  165.         }
  166.         else
  167.         {
  168.             scanf("%s%d", cmd, &i);
  169.             insert(cmd[0], i-1);
  170.         }
  171.     }
  172.     return 0;
  173. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值