zoukankan      html  css  js  c++  java
  • 后缀数组--summer-work之我连模板题都做不起

    这章要比上章的AC自动机要难理解。

    这里首先要理解基数排序:基数排序与桶排序,计数排序【详解】

    下面通过这个积累信心:五分钟搞懂后缀数组!后缀数组解析以及应用(附详解代码)  

    下面认真研读下这篇: [转载]后缀数组算法总结   里面的图片真Nice

    然后找到适合再加深认识的:后缀数组学习笔记  算法合集之《后缀数组——处理字符串的有力工具》

     

    后缀数组:通过对字符串后缀的处理得到子串的信息。

    这里我通过借鉴上面的blog和训练指南写下了一段学习程序,还有在线多模板匹配的二分查找未完成。

    用代码和测试数据的结果对比第三篇文章里的图片,有助于理解后缀数组sa的构建过程。

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 const int maxn = 1010;
      4 char s[maxn];  //字符串s,每个字符值[0,m-1]
      5 int c[maxn];  //辅助数组
      6 int x[maxn]; //rank数组,下标表示关键字的位置,值表示关键字的名次 
      7 // x代表每个后缀的前缀的种类
      8 int y[maxn]; //表示排序后的第二关键字数组,下标表示名次
      9 //值表示第二关键字的首字符位置 
     10 int sa[maxn];
     11 /* 在构造完前表示关键字数组:下标表示名次,值表示关键字首字符位置,值相同时名次根据在原串中的相对位置决定
     12  * 构造完成后表示后缀数组,下标表示名次,值表示后缀的首字符位置
     13 */
     14 void show_sort_to_get_first_sa(int n){
     15     cout << "第一次计数排序结果:" << endl; 
     16     for(int i=0; i<n; i++){
     17         cout << "" << i << "" << endl;
     18         cout << "大小 =  " << x[i] << "     " << char(x[i]) << endl;
     19         cout << "该后缀的首字符的下标(或称后缀-): " << sa[i] << endl;
     20         // aaaaaabb 
     21         // 01345627
     22     }    
     23 }
     24 void show_second_key(int n){
     25     cout << "y的结果" << endl;
     26     for(int i=0; i<n; i++){
     27         cout << y[i] << "    ";
     28     }cout << endl;
     29     /*
     30      * xaaaaabb
     31      * 70234516
     32     */    
     33 }
     34 void show_last(int n){
     35     cout << "
    sa的终值:
    ";
     36     for(int i=0; i<n; i++){
     37         cout << sa[i] << "    ";
     38     }cout << endl;
     39 }
     40 bool cmp(int *f, int x, int y, int k){
     41     return f[x]==f[y] && f[x+k]==f[y+k];
     42 }
     43 void build_sa(int m, int n){
     44     //每个字符值为[0,m-1]
     45     //字符串长度为n
     46     for(int i=0; i<=m; i++){
     47         c[i] = 0;
     48     }
     49     for(int i=0; i<n; i++){
     50         c[x[i] = s[i]]++;
     51     }   //x是s中字符的键值//c存储各键值在输入数组中出现的次数
     52     for(int i=1; i<m; i++){
     53         c[i] += c[i-1];
     54     }
     55     /*
     56      * 计算前缀和,即可知小于小于每个数的个数
     57      * aabaaaab=11211112 --> c[1]=6, c[2]=2
     58      * 计算前缀和--> c[2]=8
     59     */
     60     for(int i=n-1; i>=0; i--){
     61         sa[--c[ x[i]]] = i;
     62     }//满足第一关键字的同时满足第二关键字 
     63     /*  以上代码
     64      * 对后缀的第一位进行计数排序。
     65     */ 
     66     show_sort_to_get_first_sa(n);
     67     
     68     
     69     for(int k=1; k<=n; k<<=1){
     70         // k:当前子字符串长度 
     71         int p = 0;
     72         //当前的第二关键字可直接由上次的sa得到 
     73         for(int i=n-k; i<n; i++){
     74             y[p++] = i;
     75         }// 长度越界,第二关键字为0 
     76         for(int i=0; i<n; i++){
     77             if(sa[i] >= k){
     78                 y[p++] = sa[i] - k; //-k后移上次排序的结果 
     79                 //y存储对第二关键字排序的结果 
     80             }
     81         }//使用有第k个字符的字符串,直接排序第二关键字 
     82         //以上结合两个关键字进行总排序
     83         
     84         show_second_key(n);
     85         
     86         //基数排序第一关键字
     87         for(int i=0; i<=m; i++){
     88             c[i] = 0;
     89         } 
     90         for(int i=0; i<n; i++){
     91             c[x[y[i]]]++;
     92         }
     93         for(int i=1; i<m; i++){
     94             c[i] += c[i-1];
     95         }
     96         for(int i=n-1; i>=0; i--){
     97             sa[ --c[ x[ y[i]]]] = y[i];
     98             //y[i] = 0; 
     99         } //更新sa,
    100         swap(x, y);  //用y暂存上一轮的x(rank)
    101         /* 
    102          * 集和两个关键字排总序
    103          * 完成了关键字的合并 
    104         */
    105         p = 1;  x[sa[0]] = 0;
    106         //用已经完成的sa来更新与它互逆的rank(x) 
    107         for(int i=1; i<n; i++){
    108             x[sa[i]] = cmp(y,sa[i],sa[i-1],k)?p-1:p++;
    109         }
    110         if(p >= n) break;   //即使倍增,sa也不会改变 
    111         m = p;
    112     }
    113     
    114     show_last(n); // 
    115     
    116 }
    117 int rank[maxn];  //rank[string's num] = rank
    118 int height[maxn]; //height[rank] = len of lcp
    119 void getheight(int n){
    120     for(int i=0; i<n; i++){
    121         rank[sa[i]] = i;
    122     }//计算每个字符串字典序的排名
    123 /*
    124  * h[i]:第i个位置开始的后缀与排名在其前一名的后缀的最长公共前缀 
    125  * k=0:从首字符开始看第i个字符串和第j个字符串前面有多少是相同的
    126  * k!=0, height[rank[i]] = height[rank[i-1]] - 1,LCP长度最少是k-1,
    127  *      于是从首字符后面k-1个字符开始检查 
    128 */
    129     for(int i=0, k=0; i<n; i++){
    130         if(k) k--;
    131         /* 若k不为0, h[i]>=h[i-1]-1,最长公共前缀长度至少是k-1,从首字符后k-1开始检查
    132          * h[i-1]-1是一系列h值的最小值,包括后缀i和排在它前一个的后缀p的LCP长度 
    133         */
    134         int j = sa[rank[i] - 1];
    135         while(j+k<n && i+k<n && s[j+k]==s[i+k]) k++;
    136         //计算height[rank[i]],LCP(rank[i],rank[i]-1)
    137         height[rank[i]] = k;
    138     }
    139 }
    140 int main(){
    141     freopen("in.txt", "r", stdin);
    142     int n, m;
    143     cin >> n >> m;
    144     cin >> s;
    145     build_sa(m, n);
    146     for(int i=0; i<n; i++){
    147         for(int j=sa[i]; j<n; j++){
    148             cout << s[j] << "   ";
    149         }cout << endl;
    150     }
    151     getheight(n);
    152     return 0;
    153 }
    154 /*
    155 in.txt
    156 8 200
    157 aabaaaab
    158 */

    还没开始做题,这个知识点怕暂时要凉了。

  • 相关阅读:
    第三方模块paramiko的使用
    第三方模块paramiko的使用
    第三方模块paramiko的使用
    共识算法的比较:Casper vs Tendermint
    区块链危机!危险的共识算法
    优化的bft共识算法的设计和使用方式
    Obelisk 共识算法设计动机
    信任的 7000 年历史
    【CS231n】斯坦福大学李飞飞视觉识别课程笔记(五):图像分类笔记(下)
    【CS231n】斯坦福大学李飞飞视觉识别课程笔记(四):图像分类笔记(上)
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9503424.html
Copyright © 2011-2022 走看看