后缀数组学习ing

学习后缀数组。

宽宽发我一份英文网页,说讲的很好,于是我忍痛翻译了一晚上,卒。

第二天早上对着网页一脸懵,虽然思路什么的写的很好,简单易懂,但是那个代码实在是。。还敢更难懂一些嘛!!!想了想,还是问问卿爷吧orz

卿爷以一脸不可描述的表情给我讲了一早上后缀数组,总算明白了后缀数组究竟是啥,代码究竟是啥。期间多次差点把卿爷气死【赶紧跑】不过还是毅然决然抱着被卿爷骂死也得听懂的觉悟,看着卿爷一脸不可描述+很无奈的表情,一边顺毛一边继续听。

最后卿爷即兴发问,还好都答出来了,不然感觉已经无法在这里写博客了2333

不过后缀数组的裸题【求最长公共子串】卿爷讲了两遍我还是略模糊,看来还得翻翻大神们的博客才行。

继续修炼ing

flag不能立,要做的事情,就要自己记得啦


贴个卿爷教的模板,感觉卿爷讲过之后再看觉得好懂多了,做题做题,先过了裸题再说。。。


#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <iostream>
#include <assert.h>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 1e6 + 10;
int w[maxn];  //神奇的数组,桶排序所用,把每个字符丢到桶里
int rankk[maxn * 2];  //rank数组,与sa数组互逆,要开长度的两倍,因为排序用到了rank[i+k]
int sa[maxn];  //sa数组,嗯,写不清楚。。后缀数组里一个很神奇的数组
int height[maxn]; //记录最长公共前缀
char s[maxn]; //输入的字符串
//rankk,sa,height,都是从1存到len,0的位置不参与

void get_sa_hei()
{
  static int m[maxn], tmp[maxn];  //辅助数组,基数排序算rank和height用到
  int len = strlen(s + 1);  //len为字符串长度,字符串位置从1-len
  memset(rankk, 0, sizeof(int) * len * 2);
  for (int i = 0; i <= 255; i++) w[i] = 0; //最小要写255,以防爆炸(什么爆炸来着。。反正要开大点。。)
  for (int i = 1; i <= len; i++) w[s[i]]++; //把每个字母都丢进去
  for (int i = 1; i <= 255; i++) w[i] += w[i - 1]; //算前缀和,得出每个字母的位置
  for (int i = len; i; i--) tmp[w[s[i]]--] = i; //
  rankk[tmp[1]] = 1; //
  //如果相等,rank一样,若是不等,rank值+1,与下面注释等价
  for (int i = 2; i <= len; i++)
    rankk[tmp[i]] = rankk[tmp[i-1]] + (s[tmp[i]] != s[tmp[i-1]]);
//    if (s[tmp[i]] != s[tmp[i-1]])
//        rankk[tmp[i]] = rankk[tmp[i-1]] + 1;
//    else
//      rankk[tmp[i]] = rankk[tmp[i-1]];


  ///get sa  用基数排序
  for (int k = 1; k <= len; k <<= 1) {
    for (int i = 0; i <= len; i++) w[i] = 0;
    for (int i = 1; i <= len; i++) w[rankk[i+k]]++;
    for (int i = 1; i <= len; i++) w[i] += w[i-1];
    for (int i = len; i; i--) m[w[rankk[i+k]]--] = i;

    for (int i = 0; i <= len; i++) w[i] = 0;
    for (int i = 1; i <= len; i++) w[rankk[i]]++;
    for (int i = 1; i <= len; i++) w[i] += w[i-1];
    for (int i = len; i; i--) tmp[w[rankk[m[i]]]--] = m[i];

    m[tmp[1]] = 1;
    for (int i = 2; i <= len; i++) //最开始只有一个,这时有两个需要考虑
      m[tmp[i]] = m[tmp[i-1]] + (rankk[tmp[i]] != rankk[tmp[i-1]] || rankk[tmp[i]+k] != rankk[tmp[i-1]+k]);
//      if (rankk[tmp[i]] != rankk[tmp[i-1]] || rankk[tmp[i]+k] != rankk[tmp[i-1]+k])
//        m[tmp[i]] = m[tmp[i-1]] + 1;
//      else
//        m[tmp[i]] = m[tmp[i-1]];   //跟上面那句等价
//卿爷说那样优美,不过那一句代码好长,超过了学霸划的线,感觉美感没了。。
    for (int i = 1; i <= len; i++) rankk[i] = m[i];
  }
  for (int i = 1; i <= len; i++) sa[rankk[i]] = i; //sa数组到此求出
  //本来是没有tmp数组的,直接写的是sa,卿爷说感觉那样写还是不好,所以又定义了tmp数组
  //其实我也觉得,最后一遍赋值感觉更清晰明朗一些
  //比起sa和rank都变,我比较倾向于一个变,一个最后求
  //就是这么蠢,咬我啊【略略略】


  ///get height  一个很神奇的写法(卿爷前辈们的优化
  //模模糊糊明白一些,但是看着真的很优美啊!
  for (int i = 1, j = 0; i <= len; i++) {
    if (rankk[i] == 1) continue;
    for (j ? j-- : 0; s[sa[rankk[i]-1]+j] == s[i+j]; j++); 
    height[rankk[i]] = j;
  }
}

int main()
{
  while (scanf("%s", s + 1)) {
    get_sa_hei();
    int len = strlen(s + 1);
    for (int i = 1; i <= len; i++) {
      cout << sa[i] << "~~~" << height[i] << endl;
    }
  }
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值