最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java后綴數(shù)組之求sa數(shù)組的實例代碼

 更新時間:2018年04月24日 09:51:54   作者:小寐一覺  
后綴數(shù)組就是一個字符串所有后綴大小排序后的一個集合,然后我們根據(jù)后綴數(shù)組的一些性質(zhì)就可以實現(xiàn)各種需求。這篇文章主要介紹了Java后綴數(shù)組-求sa數(shù)組,需要的朋友可以參考下

后綴數(shù)組的一些基本概念請自行百度,簡單來說后綴數(shù)組就是一個字符串所有后綴大小排序后的一個集合,然后我們根據(jù)后綴數(shù)組的一些性質(zhì)就可以實現(xiàn)各種需求。

public class MySuffixArrayTest {
 public char[] suffix;//原始字符串
 public int n;//字符串長度
 public int[] rank;// Suffix[i]在所有后綴中的排名
 public int[] sa;// 滿足Suffix[SA[1]] < Suffix[SA[2]] …… < Suffix[SA[Len]],即排名為i的后綴為Suffix[SA[i]]
     // (與Rank是互逆運算)
 public int[] height;// 表示Suffix[SA[i]]和Suffix[SA[i - 1]]的最長公共前綴,也就是排名相鄰的兩個后綴的最長公共前綴
 public int[] h;// 等于Height[Rank[i]],也就是后綴Suffix[i]和它前一名的后綴的最長公共前綴
 public int[] ws;// 計數(shù)排序輔助數(shù)組
 public int[] y;// 第二關(guān)鍵字rank數(shù)組
 public int[] x;// rank的輔助數(shù)組
}

以下的講解都以"aabaaaab"這個字符串為例,先展示一下結(jié)果,請參考這個結(jié)果進行理解分析(這個結(jié)果圖我復(fù)制別人的,請各位默認下標減1,因為我的數(shù)組從下標0開始的)

suffix:原始字符串數(shù)組  假設(shè)原始字符串是"aabaaaab"  那這個數(shù)組對應(yīng)的值應(yīng)該是{'a','a','b','a','a','a','a','b'}
n:字符串長度 這里n是8
rank: 后綴數(shù)組的名次數(shù)組  相當于存的是第i個后綴對應(yīng)的名次是多少  比如rank[0]就是指"aabaaaab"這個后綴的名次  rank[1]指"abaaaab"這個后綴的名次
sa: 這個是和rank數(shù)組互逆的一個數(shù)組  存的是第x名的是哪個后綴  還是舉例子來說明  sa[0]指的是排名第一的后綴數(shù)組即為3  也就是"aaaab"這個數(shù)組  他對應(yīng)的rank[3]就是0。 sa[rank[i]]=i 這個式子請務(wù)必理解,理解了這個sa和rank的關(guān)系你應(yīng)該也搞懂了 
height: height[i]就是sa[i]后綴數(shù)組和sa[i-1]后綴數(shù)組的最大公共前綴的長度  height[1]指的是排名第2和排名第1的最大公共前綴 sa[1]與sa[0]即"aaab"與"aaaab"的最大公共前綴 自然一眼看出 height[1]=3
h: h[i]指的是第i個后綴與他前一名的最大公共前綴  h[0]指的就是第一個后綴數(shù)組即"aabaaaab"與他前一名即"aab"的最大公共前綴 也就是height[rank[0]]=height[3]=3 這個有點不好理解 可以暫時不理解 繼續(xù)往下看
ws: 沒什么好說的,計數(shù)排序的輔助數(shù)組
y: 存的是第二關(guān)鍵字排序  相當于第二關(guān)鍵字的sa數(shù)組
x: 你可以理解為rank數(shù)組的備份,他最開始是rank數(shù)組備份,之后記錄每次循環(huán)后的rank數(shù)組

首先來看下求sa數(shù)組的代碼,我會一段一段的說明代碼作用并在后面附上總代碼

rank = new int[n];
    sa = new int[n];
    ws = new int[255];
    y = new int[n];
    x = new int[n];
    // 循環(huán)原字符串轉(zhuǎn)換int值放入rank數(shù)組
    for (int i = 0; i < n; i++) {
      rank[i] = (int) suffix[i];
    }

上面這段代碼的作用就是初始化數(shù)組以及進行第一次計數(shù)排序,第一次循環(huán)是對rank數(shù)組賦初值,執(zhí)行完后rank數(shù)組對應(yīng)值為{97,97,98,97,97,97,97,98},大家應(yīng)該看得出來rank數(shù)組的初值就是字母對應(yīng)的ascii碼。

接下來的三段循環(huán)就是第一次計數(shù)排序了,不理解計數(shù)排序的請百度。我說下這三段循環(huán)運行的過程

for (int i = 0; i < n; i++) {
      ws[rank[i]]++;
      x[i] = rank[i];
    }
for (int i = 1; i < ws.length; i++) {
      ws[i] += ws[i - 1];
    }

這兩段循環(huán)做的是對所有出現(xiàn)值計數(shù),并備份rank數(shù)組至x數(shù)組,第一個循環(huán)運行完后ws[97]=6,ws[98]=2,第二個循環(huán)運行完后ws[97]=6,ws[98]=8

 for (int i = n - 1; i >= 0; i--) {
       sa[--ws[rank[i]]] = i;
    }

上面這段就是具體的計數(shù)排序求sa數(shù)組的代碼,大家第一次看的時候肯定是蒙的,這怎么就求出了sa呢。我第一次也是蒙的,但請保持耐心,仔細理解這段代碼。還記得前面說的公式嗎 sa[rank[i]]=i  舉個例子對于后綴"b"我們求他的sa  即sa[rank[7]]=sa[98]=7  顯然sa[98]不存在 但我們將98出現(xiàn)的次數(shù)已經(jīng)記錄在ws數(shù)組了  那么ws[98]應(yīng)該就是"b"對應(yīng)的名次了  注意不要忘記計數(shù)減1  就變成了 sa[--ws[rank[i]]] = i。至于為什么要從后向前遍歷,這里你需要仔細理解一下,否則后面根據(jù)第二關(guān)鍵字進行排序的方式你肯定會完全蒙蔽。如果有兩個rank值相同的你怎么排序呢?肯定是先出現(xiàn)的在sa數(shù)組的前面,仔細思考這個循環(huán)以及ws數(shù)組值的變化,你會明白,for循環(huán)的順序?qū)嶋H上代表了rank值相同時的排列順序。從后向前遍歷代表了rank值相同時靠后的后綴排名也靠后。

以上只是第一次計數(shù)排序,相當于只比較每個后綴數(shù)組的首字母求出了一個sa,對應(yīng)的結(jié)果如下圖

// 循環(huán)組合排序
    for (int j = 1, p = 0; j <= n; j = j << 1) {
      // 需要補位的先加入排序數(shù)組y
      p = 0;
      for (int i = n - j; i < n; i++) {
        y[p++] = i;
      }
      // 根據(jù)第一關(guān)鍵字sa排出第二關(guān)鍵字
      for (int i = 0; i < n; i++) {
        if (sa[i] >= j) {
          y[p++] = sa[i] - j;
        }
      }
      // 合并兩個關(guān)鍵字的排序
      for (int i = 0; i < ws.length; i++) {
        ws[i] = 0;
      }
      for (int i : x) {
        ws[i]++;
      }
      for (int i = 1; i < ws.length; i++) {
        ws[i] += ws[i - 1];
      }
      for (int i = n - 1; i >= 0; i--) {
        sa[--ws[x[y[i]]]] = y[i];
        y[i] = 0;
      }
      // 根據(jù)sa算出rank數(shù)組
      int xb[] = new int[n];// x數(shù)組備份
      for (int i = 0; i < n; i++) {
        xb[i] = x[i];
      }
      int number = 1;
      x[sa[0]] = 1;
      for (int i = 1; i < n; i++) {
        if (xb[sa[i]] != xb[sa[i - 1]]) {
          x[sa[i]] = ++number;
        } else if (sa[i] + j >= n && sa[i - 1] + j >= n) {
          x[sa[i]] = number;
        } else if (sa[i] + j < n && sa[i - 1] + j >= n) {
          x[sa[i]] = ++number;
        } else if (xb[sa[i] + j] != xb[sa[i - 1] + j]) {
          x[sa[i]] = ++number;
        } else {
          x[sa[i]] = number;
        }
        if (number >= n)
          break;
      }
    }

 這是求sa數(shù)組最難以理解的一段代碼,首先大家需要理解一下倍增算法的思路。第一次計數(shù)排序后我們是不是已經(jīng)知道了所有后綴數(shù)組第一個首字母的排序,既然我們知道了第一個首字母的排序那是不是相當于我們也知道了他第二個字母的順序(注意排序和順序的區(qū)別,排序是我們知道他固定的排在第幾名,順序是我們只知道他出現(xiàn)的次序,但并不知道他具體排第幾名),這是當然的,因為他們本來就是出自一個字符串,對于每個后綴他同時也可以作為他之前后綴的后綴。說起來繞口,舉個例子,比如對于"baaaab"他首字母的順序是不是對應(yīng)"abaaaab"的第二關(guān)鍵字順序。我們有了第一關(guān)鍵字的排序和第二關(guān)鍵字的排序就能求出兩個關(guān)鍵字的組合排序,跟據(jù)組合排序的結(jié)果我們還是可以延用之前的想法,對于"baaaab"第一次組合排序后我們排出來他頭兩個字母"ba"的排序,那么他同時他也可以作為"aabaaaab"的第二關(guān)鍵字的順序。整個排序的邏輯參考下圖

然后我們來分段的分析代碼

for (int i = n - j; i < n; i++) {
        y[p++] = i;
      }
      // 根據(jù)第一關(guān)鍵字sa排出第二關(guān)鍵字
for (int i = 0; i < n; i++) {
        if (sa[i] >= j) {
          y[p++] = sa[i] - j;
        }
      }

以上代碼就是求第二關(guān)鍵字的sa也就是y數(shù)組,p初始值為0,第一段循環(huán)是將需要補位的后綴排在數(shù)組最前面。

第二個循環(huán)的邏輯你需要結(jié)合前面的邏輯圖進行理解了,我們對第一關(guān)鍵字的排序結(jié)果sa進行遍歷,if(sa[i] >=j )判斷該后綴能否作為其他后綴的第二關(guān)鍵字,以第一次循環(huán)j=1為例,當sa[i]=0時代表后綴數(shù)組"aabaaaab",顯然它無法作為其他后綴的第二關(guān)鍵字。對于可以作為其他后綴第二關(guān)鍵字的,他sa的順序就是對應(yīng)的第二關(guān)鍵字的順序,sa[i] - j 求出他作為第二關(guān)鍵字的后綴放在y數(shù)組下,并且p++。這里你需要慢慢理解。

// 合并兩個關(guān)鍵字的排序
      for (int i = 0; i < ws.length; i++) {
        ws[i] = 0;
      }
      for (int i : x) {
        ws[i]++;
      }
      for (int i = 1; i < ws.length; i++) {
        ws[i] += ws[i - 1];
      }
      for (int i = n - 1; i >= 0; i--) {
        sa[--ws[x[y[i]]]] = y[i];
        y[i] = 0;
      }

以上是根據(jù)第一關(guān)鍵字排序sa和第二關(guān)鍵字排序y求出其組合排序,這段代碼相當?shù)幕逎y懂。我們可以先不理解代碼,先理解一個思路,對于兩個關(guān)鍵字排序,實際規(guī)則和兩個數(shù)字排序差不多,比如11和12比較大小,10位就是第一關(guān)鍵字,個位就是第二關(guān)鍵字,比較完10位我們求得11=12,再比較個位我們知道11<12,10位相同的話其個位的順序就是大小順序。我上面第一次計數(shù)排序時說過,計數(shù)排序for循環(huán)的順序?qū)嶋H上代表了rank值相同時的排列順序,那么這里我們怎么一次計數(shù)排序就求出兩個關(guān)鍵字合并后的順序呢?我說下我的理解,一次計數(shù)排序?qū)嶋H上包含了兩次排序,一次是數(shù)值的排序,一次是出現(xiàn)次序的排序,其規(guī)則就相當于前面11和12比較的例子,數(shù)值的排序是10位,出現(xiàn)次序的排序是個位。到這里我們就有思路了,數(shù)值的排序用第一關(guān)鍵字的排序,出現(xiàn)次序的排序用第二關(guān)鍵字的排序,這樣就能一次計數(shù)排序求得兩個關(guān)鍵字合并后的排序。上面的代碼就是這個思路的實現(xiàn)。x數(shù)組就是第一關(guān)鍵字的rank數(shù)組,我們對他進行計數(shù)。

 for (int i = n - 1; i >= 0; i--) {
        sa[--ws[x[y[i]]]] = y[i];
        y[i] = 0;
      }

這段循環(huán)就是上面所有思路的實現(xiàn),我們對第二關(guān)鍵字數(shù)組y從后進行遍歷,對于y[i]我們求出他第一關(guān)鍵字的計數(shù)排名,這個計數(shù)排名就是y[i]的排名,最后計數(shù)減1。合并關(guān)鍵字的排序成功求出。

相信你如果理解了上面所有的代碼后肯定會拍案叫絕,我本人在一遍遍琢磨這段代碼時也是熱血澎湃,簡直拜服了。這就是算法的魅力吧。

有了sa數(shù)組我們就可以求rank數(shù)組,這并不難,也就不講解了。下面附上求sa的所有代碼。

public static void main(String[] args) {
    String str = "aabaaaab";
    MySuffixArrayTest arrayTest = new MySuffixArrayTest(str.toString());
    arrayTest.initSa();// 求sa數(shù)組
  }
  public void initSa() {
    rank = new int[n];
    sa = new int[n];
    ws = new int[255];
    y = new int[n];
    x = new int[n];
    // 循環(huán)原字符串轉(zhuǎn)換int值放入rank數(shù)組
    for (int i = 0; i < n; i++) {
      rank[i] = (int) suffix[i];
    }
    // 第一次計數(shù)排序
    for (int i = 0; i < n; i++) {
      ws[rank[i]]++;
      x[i] = rank[i];
    }
    for (int i = 1; i < ws.length; i++) {
      ws[i] += ws[i - 1];
    }
    for (int i = n - 1; i >= 0; i--) {
      sa[--ws[rank[i]]] = i;
    }
    // 循環(huán)組合排序
    for (int j = 1, p = 0; j <= n; j = j << 1) {
      // 需要補位的先加入排序數(shù)組y
      p = 0;
      for (int i = n - j; i < n; i++) {
        y[p++] = i;
      }
      // 根據(jù)第一關(guān)鍵字sa排出第二關(guān)鍵字
      for (int i = 0; i < n; i++) {
        if (sa[i] >= j) {
          y[p++] = sa[i] - j;
        }
      }
      // 合并兩個關(guān)鍵字的排序
      for (int i = 0; i < ws.length; i++) {
        ws[i] = 0;
      }
      for (int i : x) {
        ws[i]++;
      }
      for (int i = 1; i < ws.length; i++) {
        ws[i] += ws[i - 1];
      }
      for (int i = n - 1; i >= 0; i--) {
        sa[--ws[x[y[i]]]] = y[i];
        y[i] = 0;
      }
      // 根據(jù)sa算出rank數(shù)組
      int xb[] = new int[n];// x數(shù)組備份
      for (int i = 0; i < n; i++) {
        xb[i] = x[i];
      }
      int number = 1;
      x[sa[0]] = 1;
      for (int i = 1; i < n; i++) {
        if (xb[sa[i]] != xb[sa[i - 1]]) {
          x[sa[i]] = ++number;
        } else if (sa[i] + j >= n && sa[i - 1] + j >= n) {
          x[sa[i]] = number;
        } else if (sa[i] + j < n && sa[i - 1] + j >= n) {
          x[sa[i]] = ++number;
        } else if (xb[sa[i] + j] != xb[sa[i - 1] + j]) {
          x[sa[i]] = ++number;
        } else {
          x[sa[i]] = number;
        }
        if (number >= n)
          break;
      }
    }
  }

總結(jié)

以上所述是小編給大家介紹的Java后綴數(shù)組之求sa數(shù)組的實例代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • 在SpringBoot框架中實現(xiàn)打印響應(yīng)的日志

    在SpringBoot框架中實現(xiàn)打印響應(yīng)的日志

    這篇文章主要介紹了在SpringBoot框架中實現(xiàn)打印響應(yīng)的日志,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java異步處理機制實例詳解

    Java異步處理機制實例詳解

    本文涉及Java編程中異步處理機制的簡單介紹和一個相關(guān)實例,相信通過這篇文章,大家能對異步處理有更多的了解。
    2017-09-09
  • 20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下
    2016-02-02
  • 你會用Java代碼模擬高并發(fā)嗎

    你會用Java代碼模擬高并發(fā)嗎

    這篇文章主要介紹了你會用Java代碼模擬高并發(fā)嗎,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • MybatisPlus3.3.0沒有MybatisPlusInterceptor類問題的解決方法

    MybatisPlus3.3.0沒有MybatisPlusInterceptor類問題的解決方法

    項目使用的是mybatis-plus-extension3.3.0依賴,然后在我使用分頁插件的時候,發(fā)現(xiàn)無法導(dǎo)入MybatisPlusInterceptor類所以本文給大家介紹了MybatisPlus3.3.0沒有MybatisPlusInterceptor類問題的解決方法,需要的朋友可以參考下
    2023-12-12
  • apache zookeeper使用方法實例詳解

    apache zookeeper使用方法實例詳解

    這篇文章主要介紹了Apache Zookeeper使用方法的有關(guān)實例及詳解,包含了其簡介和程序設(shè)計的一些思想,內(nèi)容豐富,需要的朋友可以參考下。
    2017-09-09
  • SpringMVC接收多個對象的4種方法

    SpringMVC接收多個對象的4種方法

    這篇文章主要為大家詳細介紹了SpringMVC接收多個對象的4種方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 詳解java jinfo命令

    詳解java jinfo命令

    jinfo是jdk自帶的命令,用來查看jvm的配置參數(shù).通常會先使用jps查看java進程的id,然后使用jinfo查看指定pid的jvm信息,需要的朋友可以參考下
    2021-06-06
  • Mybatis-plus和Mybatis出現(xiàn)版本不兼容的問題解決

    Mybatis-plus和Mybatis出現(xiàn)版本不兼容的問題解決

    MyBatis-Plus?與?MyBatis?之間的兼容性問題通常是由于版本不匹配引起的,本文主要介紹了Mybatis-plus和Mybatis出現(xiàn)版本不兼容的問題解決,具有一定的參考價值,感興趣的可以了解一下
    2024-08-08
  • Springboot Retry組件@Recover失效問題解決方法

    Springboot Retry組件@Recover失效問題解決方法

    在使用springboot的retry模塊時,你是否出現(xiàn)過@Recover注解失效的問題呢?不用擔(dān)心,這篇文章就來告訴你解決@Recover失效的辦法,需要的小伙伴可以參考一下
    2021-11-11

最新評論

西乌珠穆沁旗| 南宁市| 四川省| 招远市| 威远县| 盘山县| 长汀县| 肥城市| 吉首市| 蕉岭县| 年辖:市辖区| 乌拉特中旗| 镇远县| 山阳县| 宁明县| 泉州市| 交口县| 柯坪县| 邛崃市| 闽侯县| 五寨县| 永宁县| 仙桃市| 黄大仙区| 兴义市| 虎林市| 荣昌县| 莱州市| 洛浦县| 汕头市| 武安市| 略阳县| 呼伦贝尔市| 昆明市| 监利县| 楚雄市| 峨边| 土默特右旗| 化隆| 乡宁县| 徐州市|