《算法》学习笔记(5)—— 字符串的算法

版权声明:本文为博主原创,未经博主允许不得转载。 https://blog.csdn.net/weixin_36904568/article/details/89073061

一:字符串的排序

1:小整数键——键索引计数法

  1. 计算键出现的频率
  2. 将频率转换为在排序结果中的起始索引位置
  3. 在辅助数组中开始排序
public class KeyIndexSort {

    //字符串
    private Alphabet[] alphabets;
    //记录键的频率
    private int[] count;
    //临时数组
    private Alphabet[] temp;


    public KeyIndexSort(Alphabet[] alphabets,int digit,int indexLen){
        count = new int[indexLen + 1];
        temp = new Alphabet[alphabets.length];
        this.alphabets = alphabets;
        count(digit);
        change(digit);
        category(digit);
    }

    //统计频率
    public void count(int digit){
        for (int i = 0; i < alphabets.length; i++) {
            count[alphabets[i].toChar(digit) + 1]++;
        }
    }

    //转换起始索引
    public void change(int digit){
        for (int i = 0; i < count.length - 1; i++) {
            count[i+1] += count[i];
        }
    }

    //重新分类
    public void category(int digit){
        for (int i = 0; i < alphabets.length; i++) {
            int index = alphabets[i].toChar(digit);
            int tempIndex = count[index];//此时为起始索引
            count[index]++;
            temp[tempIndex] = alphabets[i];//把原来字符串的值按起始索引重新组合
        }

        for (int i = 0; i < temp.length; i++) {
            alphabets[i] = temp[i];
        }
    }

    public Alphabet[] getAlphabets() {
        return alphabets;
    }

} 

2:等长字符串——低位优先排序

从字符串的右边向左边,以每个位置作为键,用键索引法排序N次

/**
 * 低位优先
 */
public class LittleEndianSort {

    private KeyIndexSort keyIndexSort;

    private Alphabet[] alphabets;

    public LittleEndianSort(Alphabet[] alphabets){
        this.alphabets = alphabets;
    }

    public void sort(){
        int indexLen = alphabets[0].getIndexLen();
        int len = alphabets[0].R();
        //从低位数开始排序
        for (int i = len - 1; i >= 0 ; i--) {
            keyIndexSort = new KeyIndexSort(alphabets,i,indexLen);
            alphabets = keyIndexSort.getAlphabets();
        }
    }

    public void show(){
        for (int i = 0; i < alphabets.length; i++) {
            System.out.printf("%s ",alphabets[i]);
        }
    }
}    

3:变长字符串——高位优先排序

从左向右遍历字符串,把字符串分为多个开头相同的子串,将已被检察过的子串排在前面,通过递归将其他未检查的串排序

  • 在时间上:在比较小字符串时可以通过插入排序比较
  • 在空间上:如果相同前缀的字符串过多,将会耗费大量空间
/**
 * 高位优先
 */
public class HighEndianSort {
    private KeyIndexSort keyIndexSort;

    private Alphabet[] alphabets;

    public HighEndianSort(Alphabet[] alphabets){
        this.alphabets = alphabets;
    }

    public void sort(){
        int indexLen = alphabets[0].getIndexLen();
        if (indexLen >= Character.MAX_VALUE)
            indexLen = Byte.MAX_VALUE;
        //开始排序
        keyIndexSort = new KeyIndexSort(alphabets,0,indexLen,0,alphabets.length - 1);
    }

    public void show(){
        for (int i = 0; i < alphabets.length; i++) {
            System.out.printf("%s ",alphabets[i]);
        }
    }


public class KeyIndexSort {

    //字符串
    private Alphabet[] alphabets;
    //记录键的频率
    private int[] count;
    //临时数组
    private Alphabet[] temp;


    //低位排序
    public KeyIndexSort(Alphabet[] alphabets,int digit,int indexLen){
        count = new int[indexLen + 1];
        temp = new Alphabet[alphabets.length];
        this.alphabets = alphabets;
        count(digit);
        change();
        category(digit);
    }

    //高位排序
    public KeyIndexSort(Alphabet[] alphabets,int digit,int indexLen,int low,int high){
        this.alphabets = alphabets;
        if (high <= low)
            return;
//        if (high - low <= 15)
//            InSertSort.insert_sort(alphabets);
        count = new int[indexLen + 2];//空出一位处理变长的字符串
        temp = new Alphabet[alphabets.length];
        count(digit,low,high);
        change();
        category(digit,low,high);
        //从左到右排序,在以首位字符开头的字符串数组内排序
        for (int i = 0; i < indexLen; i++) {
            this.alphabets = new KeyIndexSort(this.alphabets,digit+1,indexLen,low+count[i],low+count[i+1]-1).getAlphabets();
        }
    }

    //统计频率
    public void count(int digit){
        for (int i = 0; i < alphabets.length; i++) {
            count[alphabets[i].toChar(digit) + 1]++;
        }
    }

    public void count(int digit,int low,int high){
        for (int i = low; i <= high; i++) {
            if (alphabets[i].toChar(digit) == Character.MAX_VALUE)
                count[1] ++;    //表示长度为digit的字符串已经结束,记录长度为digit的字符串的数量
            else
                count[alphabets[i].toChar(digit) + 2]++;
        }
    }

    //转换起始索引
    public void change(){
        for (int i = 0; i < count.length - 1; i++) {
            count[i+1] += count[i];
        }
    }

    //重新分类
    public void category(int digit){
        for (int i = 0; i < alphabets.length; i++) {
            int index = alphabets[i].toChar(digit);
            int tempIndex = count[index];//此时为起始索引
            count[index]++;

            temp[tempIndex] = alphabets[i];//把原来字符串的值按起始索引重新组合
        }

        for (int i = 0; i < temp.length; i++) {
            alphabets[i] = temp[i];
        }
    }

    //重新分类,分类后count记录为结束索引+1
    public void category(int digit,int low,int high){
        for (int i = low; i <= high; i++) {
            int index = alphabets[i].toChar(digit);
            int tempIndex;
            if (index == Character.MAX_VALUE) {
                tempIndex = count[0];
                count[0]++; //长度为digit的字符串个数
            }
            else {
                tempIndex = count[index + 1];
                count[index+1]++;
            }
            temp[tempIndex] = alphabets[i];//把原来字符串的值按起始索引重新组合
        }

        for (int i = low; i <= high; i++) {
            alphabets[i] = temp[i];
        }
    }

    public Alphabet[] getAlphabets() {
        return alphabets;
    }

}
//字符串插入排序
    public static Alphabet[] insert_sort(Alphabet[] arr, int low, int high, int digit){
        for (int i = low + 1; i <= high; i++) {
            if(arr[i].toString().substring(digit).compareTo(arr[i-1].toString().substring(digit)) < 0)               //比前面的数小
            {
                Alphabet temp = arr[i];
                int j;
                for (j = i; j > low && temp.toString().substring(digit).compareTo(arr[j-1].toString().substring(digit)) < 0; j--)   //找到合适的位置
                    arr[j] = arr[j-1];
                arr[j] = temp;          //把数字插进去
            }
        }
        return arr;
    }

4:改善的快速排序:三向快速排序

从高位开始,使用待比较的字符作为切分。使得左边的字符串比他小,右边的字符串比他大,中间的字符串都是以该字符开头的,可以直接跳过同一个字符再排序。

特点: 可以更好的处理等值键,公共前缀键,小数组


/**
 * 三向快速排序
 */
public class Quick3Sort {

    private Alphabet[] alphabets;

    public Quick3Sort(Alphabet[] arr){
        alphabets = arr;
        quick_sort(alphabets,0,arr.length - 1,0);
    }

    //快速排序
    public static Alphabet[] quick_sort(Alphabet[] arr, int low, int high, int digit){
        //        if(high <= low + 10)    //对于小数组,使用插入排序
//        {
//            InSertSort.insert_sort(arr,low,high,digit);
//            return arr;
//        }
        if (high <= low)
            return arr;
        int key;    //记录当前的切分字符
        if (arr[low].toChar(digit) == Character.MAX_VALUE)
            key = -1;
        else
            key = arr[low].toChar(digit);

        int[] pivot = find_pivot(arr,low,high,key,digit);       //获取基准值的下标,便于分割数组
        quick_sort(arr,low,pivot[0]-1,digit);   //左边排序
        if (key >= 0)
            quick_sort(arr,pivot[0],pivot[1],digit+1);  //中间排序,忽略首字符
        quick_sort(arr,pivot[1]+1,high,digit);  //右边排序
        return arr;
    }

    //找基准值的下标
    public static int[] find_pivot(Alphabet[] arr,int low,int high,int key,int digit){
        int i = low+1;
        while (i <= high)
        {
            //记录当前下标的字符
            int temp = arr[i].toChar(digit);
            if (temp == Character.MAX_VALUE)
                temp = -1;
            //当前字符和切分的字符进行比较,保证左边比较小,右边比较大
            if (temp < key)
                SortUtil.swap(low++,i++,arr);
            else if (temp > key)
                SortUtil.swap(i,high--,arr);
            else
                i++;
        }
        int[] pivot = {low,high};
        return pivot;
    }

    public void show(){
        for (int i = 0; i < alphabets.length; i++) {
            System.out.printf("%s ",alphabets[i]);
        }
    }
}

二:字符串的查找

1:单词查找树

根据单词的索引对应的字符,在指定的子树内逐层查找

  • 适用于字母表和键较小的情况,需要耗费空间
   //获取键值
    public T get(String key){
        StringTreeNode<T> node = get(key,root,0);
        if (node == null)
            return null;
        return node.getValue();
    }

    private StringTreeNode<T> get(String key,StringTreeNode<T> node,int digit){
        //在串的范围内查找
        if (node == null)
            return null;
        if (digit == key.length())
            return node;
        int index = alphabet.toIndex(key.charAt(digit));
        return  get(key,node.getNodes()[index],digit+1);//在对应的子树查找
    }
}

2:三向查找树

根据单词的索引对应的字符,分别在左子树,中子树,右子树内查找
遍历时对子树的顺序有要求,同时每个结点自身带有一个键

 //获取键值
    public T get(String key){
        ThreeStringTreeNode<T> node = get(key,root,0);
        if (node == null)
            return null;
        return node.getValue();
    }

    public ThreeStringTreeNode<T> get(String key,ThreeStringTreeNode<T> node,int digit){
        if (key.length() == 0)
            return root;
        if (node == null)
            return null;
        int index = alphabet.toIndex(key.charAt(digit));
        //对三个子节点进行比较
        if (alphabet.toChar(index) < node.getKey())
            return get(key, node.getLeft(), digit);
        else if (alphabet.toChar(index) > node.getKey())
            return get(key, node.getRight(), digit);
        //如果是中间结点,看看是不是够长度了
        else if (digit < key.length() - 1)
            return get(key, node.getMid(), digit+1);
        else
            return node;
    }

三:字符串的查找

1:暴力查找

逐个对照文本和模式串


    public int search(String pattern){
        int index = -1;
        int pLen = pattern.length();
        //i指向文本的开头
        for (int i = 0; i < len; i++) {
            index = i;
            //j指向模式串的开头
            for (int j = 0; j < pLen; j++) {
                if (text.charAt(i + j) != pattern.charAt(j))
                {
                    index = -1;
                    break;
                }
            }
            if (index != -1)
                return index;
        }
        return index;
    }

    public int searchClear(String pattern){
        int index = -1;
        int pLen = pattern.length();
        int i,j;
        //i指向文本的末尾,j指向模式串的开头
        for (i = 0,j = 0; i < len && j <pLen; i++) {
            if (text.charAt(i) != pattern.charAt(j)){
                i -= j;
                j = 0;
            }
            else
                j++;
        }
        if (j == pLen)
            return i - pLen;
        return index;
    }

2:KMP算法

提前判断在文本中重新查找的定位,不会重新回退文本指针,而是回退模式指针。

DFA:有限状态转换机——dfa[文本字符][模式字符]

定义:

模式中的每个字符对应一个状态,每个状态可以转换为任意字符。其中只有一种转换 “从左指向右” 是匹配的,其他转换 “指向左侧” 是不匹配的。

转换过程:
  1. 从文本前进,检查字符( i+1)
  2. 对于文本的字符,检查转换机。
  • 如果模式的字符匹配,则转换机向右移动(j+1)
  • 如果模式的字符不匹配,则转换机向左移动
    (j = dfa[text.charAt(i)][pattern.charAt(j)])
  1. 如果转换机到达停止状态,则说明匹配了模式字符串
  2. 如果在文本结束后还未到达停止状态,则没有匹配
构造:扫描模式字符串

忽略首位:需右移
扫描 第1位到第j-1位
忽略最后一位:已经匹配失败了

例子:ABABC
  • 状态0:A-[开始状态]                ——> 开始右移

  • 状态1:A B-[0] [0]                    ——> 没有相同的,重新开始匹配

  • 状态2:A B-[0] A-[0] [1]            ——>与状态0相同,从状态1继续匹配

  • 状态3:A B-[0] A-[0] B-[1] [2]    ——>与状态1相同,从状态2继续匹配

  • 状态5:A B-[0] A-[0] B-[1] C-[停止状态]——>匹配结束

    //创建状态机
    public void createDFA(String pattern){
        dfa = new int[alphabet.R()][pattern.length()];

        //忽略模式串的首位,状态机启动
        dfa[pattern.charAt(0)][0] = 1;

        for (int lastIndex = 0,nextIndex = 1; nextIndex < pattern.length(); nextIndex++) {

            //若匹配失败,状态机返回上一状态
            for (int alphabetIndex = 0; alphabetIndex < alphabet.R(); alphabetIndex++) {
                dfa[alphabetIndex][nextIndex] = dfa[alphabetIndex][lastIndex];
            }

            //匹配成功,状态机向前继续走 2,3,4....
            dfa[pattern.charAt(nextIndex)][nextIndex] = nextIndex + 1;

            //记录状态机的上一状态
            lastIndex = dfa[pattern.charAt(nextIndex)][lastIndex];
        }
    }

查找

public int search(String pattern){
        createDFA(pattern);
        int index = -1;
        int pLen = pattern.length();
        int i,j;
        //i指向文本的末尾,j指向模式串的开头
        for (i = 0,j = 0; i < len && j <pLen; i++) {
            j = dfa[text.charAt(i)][j];
        }
        if (j == pLen)
            index = i - pLen;
        return index;
    }

2:Boyer-Moore算法

从右向左扫描模式字符串,在文本中回退

匹配失败:

  • 字符不被包含:应该检查文本的下一位,将模式字符串与已知模式字符串对齐。否则字符会重叠
  • 字符被包含:使用right数组(记录字符最右位置),检查文本的第x位,将模式字符串与字符出现的最右位置对齐。否则该字符会与更右边的字符重叠
  • 保证字符向右移动
   public void initialize(String pattern){
        for (int i = 0; i < pattern.length(); i++) {
            char c = pattern.charAt(i);
            right[c] = i;
        }
    }

    public int search(String pattern){
        initialize(pattern);
        int index = -1;
        int pLen = pattern.length();
        int skip;
        //i指向文本的开头,j指向模式串的末尾
        for (int i = 0; i <= len - pLen; i += skip) {
            skip = 0;
            for (int j = pLen - 1; j >= 0; j--) {
                char c = text.charAt(i + j);
                if (c != pattern.charAt(j)){
                    //对齐模式字符串,模式串必须向前移动,避免重叠
                    skip = j - right[c];
                    if (skip <= 0)
                        skip = 1;
                   	break;
                }
            }
            //匹配
            if (skip == 0)
                return i;
        }
        return index;
    }

3:Rabin-Karp算法

长度为M的字符串,对应于R进制的M位数。需要用一张大小为Q的散列表保存键

  1. 计算模式字符串的散列值
  2. 计算文本中与模式字符串相同长度的子字符串的散列值,如果散列值和模式字符串相同再验证是否匹配。

基本方法:

  • 除留取余法:适用于5位以内的数值
  • Hornor:适用于多位数值,但是成本高
     for (int i = 0; i < target.length(); i++) {
            hash = (R * hash + target.charAt(i)) % prime;
        }
  • 高效Hornor:[i~M-1]的字符串的值=它减去第一个数,乘R,加上最后一个数。
 textHash = hash(text,pLen);
 textHash = (textHash + Q - RM * text.charAt(i - pLen) % Q) % Q;
 textHash = (textHash * R + text.charAt(i)) % Q;

查找

	private boolean check(int i) {
        return true;
    }

    private boolean check(int i,String pattern) {
        for (int j = 0; j < pattern.length(); j++)
            if (pattern.charAt(j) != text.charAt(i + j))
                return false;
        return true;
    }

    //获得第M-1个R 余 Q
    private void initialRM(String pattern){
        int pLen = pattern.length();
        this.RM = 1;
        for (int i = 0; i < pLen; i++) {
            RM = (R * RM) % Q;
        }
    }

    //hornor哈希
    private long hash(String key, int M) {
        long h = 0;
        for (int j = 0; j < M; j++)
            h = (R * h + key.charAt(j)) % Q;
        return h;
    }

    public int search(String pattern){
        initialRM(pattern);
        int pLen = pattern.length();
        this.textHash = hash(text,pLen);
        long pHash = hash(pattern,pLen);
        //一开始就命中
        if (pHash == textHash && check(0))
            return 0;
        for (int i = pLen; i < len; i++) {
            //减去第一个数
            textHash = (textHash + Q - RM * text.charAt(i - pLen) % Q) % Q;
            //加上最后一个数
            textHash = (textHash * R + text.charAt(i)) % Q;
            if (pHash == textHash)
                if (check(i - pLen + 1))
                return i - pLen + 1;
        }
        return -1;
    }

四:字符串的压缩

系统的输入输出都是基于8位的字节流,也就是基于二进制的比特流。能够通过压缩将比特流最小化。

1:X位编码

对于字母表的种类和大小固定的字符串,可以直接针对字母表进行编码。把压缩个数从原来八位编码减少到N位编码。

  • 2种字母(正负对错):单位编码(0,1)
  • 4种字母(基因碱基):双位编码(00,01,10,11)
  • 8种字母(八仙过海):三位编码(000,001,010,011,100,101,110,111)
  • 2N种字母:log2N位编码

(1)压缩

  1. 确定字符串的字符数量,方便解码顺利进行
  2. 根据字符在字母表的索引和编码位数,压缩为01编码

(2)解压缩

  1. 根据编码位数,读取字符的索引
  2. 根据字母表和字符索引,解压缩对应字符

2:游程编码

游程是指连续的0或1的字符串。对于短游程相对较少,长游程较多的长比特流(位图),可以直接针对游程编码,将连续的0或1直接表示为它的个数,并且使用4位或8位对个数编码、输出。

(1)压缩

  1. 从0开始,读取0或1,并记录个数
  2. 若个数超过范围,则压缩个数,再压缩0,方便继续读取该字符
  3. 若字符不同,则压缩个数

(2)解压缩

从0开始,根据个数,不断输出0或1

3:霍夫曼编码

对于一段普通的字符串,将出现频率高的字符用较少的位数编码,出现频率较低的字符用较多的位数编码,同时字符编码都不是其他字符编码的前缀码。

本质:两个链接的单词查找树

把0和1看为单词查找树中的键,把字符看为单词查找树对应的值,则构造单词查找树相当于对字符进行霍夫曼编码:每个字符的编码就是从根节点到该叶子节点的路径。

(1)构造二叉树

  1. 读取字符串,获取N个包含字符及其出现的频率的树,加入优先队列
 //统计频率
    public void initFrequence(){
        for (int i = 0; i < input.length; i++) 
            frequences[input[i]]++;
    }
  1. 不断取两个最小的树,生成一个新的父节点和一棵新的树,树的根节点的频率为他们的和。
  2. 只剩下一个结点时,为单词查找树的根节点。
   //创建树
    public void createTree(){
        //初始化多个树
        for (int i = 0; i < input.length; i++) {
            int freq = frequences[input[i]];
            if (freq == 0 ) //忽略无关的字符
                continue;
            HuffmanNode node = new HuffmanNode(input[i],freq);
            pq.insert(node);
        }
        //开始创建
        while (pq.getSize() > 1){
            HuffmanNode left = pq.delMaximum();
            HuffmanNode right = pq.delMaximum();
            HuffmanNode parent = new HuffmanNode('\0',left.getFrequence()+right.getFrequence(),left,right);
            pq.insert(parent);
        }
        root = pq.delMaximum();
    }

(2)构建映射表

从根节点开始记录路径,向左移动的同时字符+0,向右移动的同时字符+1,遇到叶子节点则获得目标字符及其路径编码,保存在映射表中

 //建立映射
    private void codeTree(HuffmanNode node,String s){
        if (node.isLeaf())
        {
            int index = alphabet.toIndex(node.getValue());
            codes[index] = s;
            return;
        }
        codeTree(node.getLeft(),s+'0');
        codeTree(node.getRight(),s+'1');
    }

(3)传输树

  1. 对树进行前序遍历,遇到内部结点输出0,遇到叶子结点输出1并且将其字符的ASCALL八码编码输出。
    //输出树
    private void transferTree(HuffmanNode node){
        if (node.isLeaf())
        {
            treeCode += "1";
            int index = alphabet.toIndex(node.getValue());
            treeCode += codes[index];
            return;
        }
        treeCode += "0";
        transferTree(node.getLeft());
        transferTree(node.getRight());
    }
  1. 遍历字符串,遇到1则构造叶子节点并加入字符,遇到0则构造父节点并递归构造左右子树。
   //解码树
    private HuffmanNode decodeTree() {
        if (index == input.length)
            return null;
        if (input[index] == '1') {
            return new HuffmanNode(input[index], 0, null, null);
        } else {
            index++;
            HuffmanNode left = decodeTree();
            index++;
            HuffmanNode right = decodeTree();
            return new HuffmanNode('\0', 0, left, right);
        }
    }

(3)压缩

  1. 构造编码单词查找树
  2. 根据树构造映射表
  3. 根据映射表编码
 //根据明文,映射表压缩
    public String compress(){
        StringBuffer output = new StringBuffer();
        for (int i = 0; i < input.length; i++) {
            int index = alphabet.toIndex(input[i]);
            String code = codes[index];
            for (int j = 0; j < code.length(); j++) {
                if (code.charAt(j) == '1')
                    output.append('1');
                else
                    output.append('0');
            }
        }
        return output.toString();
    }

(4)解压缩

  1. 读取一棵树
  2. 使用该树解码:从根节点开始,遇到0向左移动,遇到1向右移动,遇到叶子节点则获得字符。
 //根据密文解压缩
    public String expand() {
        StringBuffer output = new StringBuffer();
        HuffmanNode node = root;
        for (int i = 0; i < input.length;) {
            while (!node.isLeaf()){
                if (input[i++] == '0')
                    node = node.getLeft();
                else
                    node = node.getRight();
            }
            output.append(node.getValue());
            node =root;
        }
        return output.toString();
    }

4:LZW编码

维护一张字符串键和定长编码的编译表,用十六进制表示8位编码。

  • 00-7F:符号表中的128个单字符的键(最前补0)
  • 80:保留80,作为文本结束的标志
  • 81-FF:将其他的编码值分配给子字符串

(1)压缩

  1. 找出输入的字符串在符号表中的最长前缀,并输出前缀的编码。
  2. 继续查找下一字符,将前缀的编码和该字符的组合作为字母表的新建,扩展编码递增。
public static String compress(){
        StringBuffer result = new StringBuffer();//编码结果
        int code = EOF;  //用80标志文件结束
        while (input.length() > 0){
            //获取最长前缀,输出编码
            String prefix = tree.longestPrefix(input);
            result.append(tree.get(prefix));
            //如果文本还未结束并且在字母表范围中,继续查找下一字符组合,扩展编码递增
            if (prefix.length() < input.length() && code < sum)
                tree.put(input.substring(0,prefix.length()+1),code++);
            input = input.substring(prefix.length());
        }
        result.append(R);   //输出结束标记
        return result.toString();
    }

(2)解压缩

  1. 根据编码X,在符号表中找到和X匹配的字符串S1,再读取下一个编码获取S2
  2. 将符号表的下一个扩展值递增,键设为S1+S2的首字母
 //构造逆表
    public static String[] initST(){
        String[] st = new String[sum];
        //初始化字母表对应的映射
        for (int i = 0; i < R; i++)
            st[i] = ""+(char)i;
        st[EOF] = " ";  //结束标记
        return st;
    }

    public static String expand(){
        StringBuffer result = new StringBuffer();//编码结果
        String[] st = initST();
        int found = EOF+1;  //记录扩展编码

        //从密文第一个编码开始,根据逆表获取字符
        int index = alphabet.toChar(input.charAt(0));
        String value = st[index];

        for (int i = 1; i < input.length(); i+=width) {
            //输出编码对应字符串
            result.append(value);
            //获取下一个编码
            index = alphabet.toChar(input.charAt(i));
            //超出字母表范围
            if (index == R)
                break;
            String temp = st[index];
            //如果前缀码不可用,则根据上一个字符串的首字母获取新的前缀码
            if (index == found)
                temp = value + value.charAt(0);
            //在编码范围之内,则扩展编码,添加新的键值对
            if (found < sum)
                st[found++] = value + temp.charAt(0);
            value = temp;
        }
        return result.toString();
    }

猜你喜欢

转载自blog.csdn.net/weixin_36904568/article/details/89073061