Encode and Decode Strings

BitTorrent 有一个P2P跨平台的序列化规范,简单易懂,叫Bencode, 简单讲就是 “长度 + 分隔符 + 字符串” 的 encoding.

思想上讲和 OS 的 file system 是非常像的,在实际 data 最前面的 header 里会存 metadata,告诉你下一段数据的内存地址或者offset。

    // Encodes a list of strings to a single string.
    public String encode(List<String> strs) {
        StringBuilder sb = new StringBuilder();
        for(String s: strs){
            sb.append(s.length()+"/"+s);
        }
        return sb.toString();
    }

    // Decodes a single string to a list of strings.
    public List<String> decode(String s) {
        List<String> list = new ArrayList<String>();
        StringBuilder sb = new StringBuilder(s);
        while(sb.length()>0){
            int index = sb.indexOf("/");
            int len = Integer.valueOf(sb.substring(0, index));
            list.add(sb.substring(index+1, index+1+len));
            sb = new StringBuilder(sb.substring(index+1+len));
        }
        return list;
    }
}
  • Escape符的正确定义是:看到了就跳过,但是 escape 符后面的一定是有效字符。

  • 于是一个落单的 '#' 代表单词 delimiter ;

  • 原字符串的 '#' 变成了 '/ + #';这样保证可以加进去但是又不会作为 '#' 单独被判断;

  • 原字符串的 '/' 就变成了 '//',实际意义是 "escape 符 + 有效字符"。

    • System.out.println("\"); output: \

  • 在序列化多叉树的时候就可以允许特殊字符的存在.

  • 犯错一: Escape 符号decode完后要自增1, if(sb.charAt(i)=='/') tmp.append(sb.charAt(i++));

    // Encodes a list of strings to a single string.
    public String encode(List<String> strs) {
        StringBuilder sb = new StringBuilder();
        for(String s : strs){
            for(int i=0;i<s.length();i++){
                if(s.charAt(i)=='/'){
                    sb.append("//");
                }
                else if(s.charAt(i)=='#'){
                    sb.append("/#");
                }
                else
                    sb.append(s.charAt(i));
            }
            sb.append("#");
        }
        return sb.toString();
    }

    // Decodes a single string to a list of strings.
    public List<String> decode(String s) {
        List<String> list = new ArrayList<String>();
        StringBuilder sb = new StringBuilder(s);
        StringBuilder tmp = new StringBuilder();
        System.out.println(sb.toString());
        for(int i=0;i<sb.length();i++){
            if(sb.charAt(i)=='/'){
                tmp.append(sb.charAt(i+1));
                i++;
            }
            else if(sb.charAt(i)=='#'){
                list.add(tmp.toString());
                tmp.setLength(0);
            }
            else
                tmp.append(sb.charAt(i));
        }
        return list;
    }
}

Unique Word Abbreviation

这题本身非常简单,但是写 sample test case 的人有点智障。。。得看着挂了的 test case 一点一点猜才能知道这题到底是想干啥。

这题的意思是,我们要看的是一个 key 到底是否有效; 给你一个 word,如果字典里面完全没有一样的 abbreviation -> true; 如果有一样的 abbreviation 但是全和你的 word 是一样的单词,也返回 true;其他的都是 false;

注意当dictionary里有两个词有一样的缩写的时候,set value 为空. 当查询key的时候一定返回false.

public class ValidWordAbbr {

    HashMap<String, String> map;
    public ValidWordAbbr(String[] dictionary) {
        map = new HashMap<String, String>();
        for(String s : dictionary){
            String key = getKey(s);
            if(map.containsKey(key)){
                if(!map.get(key).equals(s))
                    map.put(key, "");
            }
            else{
                map.put(key,s);
            }
        }
    }

    public String getKey(String s){
        if(s.length()<=2)
            return s;
        return s.charAt(0)+ Integer.toString(s.length()-2) +s.charAt(s.length()-1);
    }

    public boolean isUnique(String word) {
        String key = getKey(word);
        return !map.containsKey(key) || map.get(key).equals(word);
    }
}


// Your ValidWordAbbr object will be instantiated and called as such:
// ValidWordAbbr vwa = new ValidWordAbbr(dictionary);
// vwa.isUnique("Word");
// vwa.isUnique("anotherWord");

Generalized Abbreviation

典型的搜索问题,对于 "word" ,该词的搜索图和状态空间如图所示:

第一遍 AC 的代码~

  • 错误1:不能盲目用 indexOf(len) 去找下一个起点,因为 len 作为一个数字是可以重复的。

  • 错误2:用 lastIndexOf 也不行,因为数字不都是一位数,有肯能会出现重复数字的两位数。

  • 因此, 最稳的做法是,pos 一定新 string 的起点,然后加上数字 string 的长度,再加一。

public class Solution {
    public List<String> generateAbbreviations(String word) {
        List<String> result = new ArrayList<String>();
        helper(word, 0, 0, "",result);
        return result;
    }

    public void helper(String word, int start, int count, String cur, List<String> result){
        if(start==word.length()){
            if(count>0)
                cur+=count;
            result.add(cur);
            return;
        }

        helper(word, start+1, count+1, cur, result);
        helper(word, start+1, 0, cur+ (count>0?count:"")+word.charAt(start), result);


    }
}

(Google)字符串嵌套压缩 / 解压缩

变种1:

Decode String

http://www.1point3acres.com/bbs/thread-189365-1-1.html

上来大概问了5分钟background然后直接上题,一个string decompression的题。。不知道是不是原题反正没见过。。题目如下.

2[abc]3[a]c => abcabcabcaaac; 2[ab3[d]]2[cc] => abdddabdddcc 输入 输出 一开始用了一个栈,写着写着嵌套的逻辑卡住了,最后用俩stack解决。。然后follow-up问的是不要输出string而是输出解压后的K-th character,主要也还是嵌套情况就从内到外把疙瘩解开以后再算。。然后我问俩问题就结束了。整体感觉妹子面试官人很nice 反应很快而且不是特别picky的那种。

变种2:

“3a2[mtv]ac”, decompress to: aaamtvmtvac,括号可以嵌套。 这个我觉得不是很难,大概花了15分钟理清了思路并写好了代码,大概就是找匹配括号递归解,面试官也找不到bug表示认同。

但吊诡的地方来了,面试官说把这种字符串compress回去...这显然有多种情况,于是我问是不是要求压缩后最短,面试官说肯定越短越好。 比如对于aaaa, 肯定4a比2[aa]好。

Encode String with Shortest Length

Input: "abbbabbbcabbbabbbc"
Output: "2[2[abbb]c]"
  • 仔细看还是有DP的影子, 字符串被compress到最短的时候, 子字符串(repeat pattern)也一定是由优化字符串构成. 当前问题由最优子问题构成, DP.

  • dp[i][j] 代表从i到j字符串的最优表达方式.

  • dp[i][j]的构成方式有三种

    • dp[i][j]在j-i<4的时候就是substring(i, j)

    • dp[i][j] 由 dp[i][k] 和 dp[k+1][j]两个字符串拼接而成

    • dp[i][j] 有 从i开始的重复字符串, 可以压缩得到. substr.length()/rptStr.length() + "[" + dp[i][k] + "]";

  • 注意下标, 注意拼接的时候用dp[i][k],而不是 repeat String.

public class Solution {
    public String encode(String s) {
        String[][] dp = new String[s.length()][s.length()];
         for(int l=0;l<s.length();l++) {
            for(int i=0;i<s.length()-l;i++) {
                int j = i+l;
                String substr = s.substring(i,j+1);
                dp[i][j] = substr;
                if(j-i<4){
                    continue;
                }
                else{
                    for(int k =i;k<j;k++){
                        if(dp[i][k].length()+dp[k+1][j].length()<dp[i][j].length())
                            dp[i][j] = dp[i][k] + dp[k+1][j];
                    }

                    for(int k=i;k<=j;k++){
                        String rptStr = s.substring(i, k+1);
                        if(substr!=null && substr.length()%rptStr.length()==0 && substr.replaceAll(rptStr, "").length()==0){
                            String ss = substr.length()/rptStr.length() + "[" + dp[i][k] + "]";
                          if(ss.length() < dp[i][j].length()) {
                            dp[i][j] = ss;
                          }
                        }
                    }
                }
            }
         }
        return dp[0][s.length()-1];
    }
}

results matching ""

    No results matching ""