需要说的都已经在源代码中注明了!
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- using namespace std;
- int cmp(int *r,int a,int b,int l){
- return r[a]==r[b]&&r[a+l]==r[b+l];
- }
- #define M 100
- int wa[M],wb[M],wv[M],ws[M];
- int SA[M];
- char s[M]=“aabaaaab”;
- void print(char *label,int *arr,int n){
- puts(label);
- for(int i=0;i<n;i++)
- printf(“%d “,arr[i]);
- puts(“”);
- }
- void da(char *r,int *sa,int n,int m){
- int i, j, p, *x = wa, *y = wb;
- for (i = 0; i < m; i++)
- ws[i] = 0;
- for (i = 0; i < n; i++)
- ws[x[i] = r[i]]++;
- for (i = 1; i < m; i++)
- ws[i] += ws[i – 1];
- for (i = n – 1; i >= 0; i–)
- sa[–ws[x[i]]] = i;
- print(“array sa:”,sa,n); //**************print**************
- for (j = 1, p = 1; p < n; j *= 2,m=p)
- {
- //对第二关键字进行排序:由上一次的结果 直接算出来
- for (p = 0, i = n – j; i < n; i++) y[p++] = i;
- /*
- * y [p++] 表示已经移出表达的范围外面了,所以一定是最小的,
- * 排在前面,但由于我们实际振作的是下标,所以y存储的也是下标,
- * 而且第一个出界的是n-j.实际上排名不分先后
- *
- * 下面的sa[i]-j 即对已经排好序的元素,进行降级处理,表示
- * 及向后移动了j位,实际上这就是后缀的第二关键字 由基数排序
- * 的特点,如果第二关键字有序了,那么排到第一关键字有序的时候,
- * 整个序列就有序了这里我还没有相通的一点是 if(sa[i]>=j)sa[i]-j
- * 这样做了以后,会不会有重复的值出现呢? 其实不会,这里它把
- * sa[i]值<j的过滤了 我们一定要记住sa[i]储存的是下标,sa[i]小于j,实际
- * 上是最前面的数。 这就有点像滑动窗口的感觉 ! 指定窗口的大小
- * 为s.size(),窗口以倍增的方式向后滑动,滑动窗口方向 前面 越界
- * 的数值设为0,滑动窗口后面越界的数值扔掉。这样就保证有序了!
- *
- * 好吧,这段代码花了我几天的时间,如果今天不回头看看 基数
- * 排序,可能还给我几天都搞不懂的! 唉!基础是硬伤啊!!!
- * */
- for (i = 0; i < n; i++)
- if (sa[i] >= j) y[p++] = sa[i] – j;
- print(“array y:”,y,n); //**************print**************
- //对第一关键字进行排序
- for (i = 0; i < n; i++) wv[i] = x[y[i]];
- print(“wv:”,wv,n);//**************************************
- /*
- * 如果前面理解了,wv[i]=x[y[i]]也就好理解了
- *实际上:论文里面已经说得很清楚了:x[i]相当于上次排序的rank数组,
- ,而我们的y相当于局部SA数组(只考虑到了一个关键字)
- *如果SA[i]=k,那么一定有 rank[k]=i, 这样的话,这段代码就相当于 rank[sa[i]]=k,
- *就 相当于把数据从小到大扔到待排序的桶里去(联系基数排序来理解 )
- * */
- /*
- * 这下面的几行与第一次排序几乎是一模一样的,没有什么好说的
- *
- * */
- for (i = 0; i < m; i++) ws[i] = 0;
- for (i = 0; i < n; i++) ws[wv[i]]++;
- for (i = 1; i < m; i++) ws[i] += ws[i – 1];
- for (i = n – 1; i >= 0; i–) sa[–ws[ wv[i]]] = y[i];
- /*
- * 完成排序,通过排序的结果求新的排名
- * swap(x, y) 这里就不怎么好理解 x[i]相当于上次排序的rank数组,
- * 而我们的y相当于局部SA数组(只考虑到了一个关键字)
- * 交换过后,x就变成局部SA数组了,而y就变成了rank数组
- * 为了思路连贯,方便理解,我把代码调整一下*/
- // for ( swap(x, y),p = 1, x[sa[0]] = 0, i = 1; i < n; i++)
- // x[sa[i]] = cmp(y, sa[i – 1], sa[i], j) ? p – 1 : p++;
- for ( p = 1, y[sa[0]] = 0, i = 1; i < n; i++)
- y[sa[i]] = cmp(x, sa[i – 1], sa[i], j) ? p – 1 : p++;
- swap(x, y);
- /*
- * 这样的话,个人觉得好理解多了!
- * 首先这几行代码的作用是:通过SA来计算新的rank值
- * cmp(x, sa[i – 1], sa[i], j) ? p – 1 : p++;
- * sa[i-1],sa[i]就表示排序后两个相邻的后缀的首地址
- *
- * x[sa[i-1] ,x[sa[i]]就表示两个排名相邻的元素
- * 其实我个人觉得 x[]数组 既表示了排名,又兼顾表示的原字符串
- * 这里比较抽象,不好理解
- * */
- }
- }
- int main(){
- int n=strlen(s);
- int m=128;
- da(s,SA,n+1,m);
- print(“sa:”,SA,n+1);//**************print**************
- return 0;
- }
- /*
- * sa:
- 8 3 4 5 0 6 1 7 2
- * */
转载于:https://blog.51cto.com/sbp810050504/1040122
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110293.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...