[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]

【问题描述】[中等]

[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第1张图片

【解答思路】

1. 动态规划

动态规划流程
第 1 步:设计状态
dp[i] 表示字符串的前 i 个字符的最少未匹配数。

第 2 步:状态转移方程
假设当前我们已经考虑完了前 i -1个字符了,对于前 i 个字符对应的最少未匹配数:

第 i 个字符未匹配,则 dp[i] = dp[i+1] + 1,即不匹配数加 1;
遍历前 i -1个字符,若以其中某一个下标 j 为开头、以第 i 个字符为结尾的字符串正好在词典里,则 dp[i] = min(dp[ i ], dp[ j ]) 更新 dp[i]。

第 3 步:考虑初始化
int[] dp = new int[n+1];
dp[0] = 0;
第 4 步:考虑输出
dp[n];

时间复杂度:O(N^3) 空间复杂度:O(N)

class Solution {
    public int respace(String[] dictionary, String sentence) {
        Set<String> dic = new HashSet<>();
        for(String str: dictionary) dic.add(str);

        int n = sentence.length();
        //dp[i]表示sentence前i个字符所得结果
        int[] dp = new int[n+1];
        for(int i=1; i<=n; i++){
            dp[i] = dp[i-1]+1;  //先假设当前字符作为单词不在字典中
            for(int j=0; j<i; j++){
                if(dic.contains(sentence.substring(j,i))){
                    dp[i] = Math.min(dp[i], dp[j]);
                }
            }
        }
        return dp[n];
    }
}


2. Trie字典树优化

[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第2张图片
[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第3张图片
复杂度分析
[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第4张图片

class Solution {
    public int respace(String[] dictionary, String sentence) {
        int n = sentence.length();

        Trie root = new Trie();
        for (String word: dictionary) {
            root.insert(word);
        }

        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for (int i = 1; i <= n; ++i) {
            dp[i] = dp[i - 1] + 1;

            Trie curPos = root;
            for (int j = i; j >= 1; --j) {
                int t = sentence.charAt(j - 1) - 'a';
                if (curPos.next[t] == null) {
                    break;
                    //单词终结标志
                } else if (curPos.next[t].isEnd) {
                    dp[i] = Math.min(dp[i], dp[j - 1]);
                }
                if (dp[i] == 0) {
                    break;
                }
             
                curPos = curPos.next[t];
            }
        }
        return dp[n];
    }
}

class Trie {
    public Trie[] next;
    public boolean isEnd;
    
    public Trie() {
        next = new Trie[26];
        isEnd = false;
    }

    public void insert(String s) {
        Trie curPos = this;

        for (int i = s.length() - 1; i >= 0; --i) {
            int t = s.charAt(i) - 'a';
            if (curPos.next[t] == null) {
                curPos.next[t] = new Trie();
            }
            curPos = curPos.next[t];
        }
        //给遍历的时候的单词终结标志
        curPos.isEnd = true;
    }
}



3. 字符串哈希

[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第5张图片
复杂度分析
[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第6张图片

class Solution {
    static final long P = Integer.MAX_VALUE;
    static final long BASE = 41;

    public int respace(String[] dictionary, String sentence) {
        Set<Long> hashValues = new HashSet<Long>();
        for (String word : dictionary) {
            hashValues.add(getHash(word));
        }

        int[] f = new int[sentence.length() + 1];
        Arrays.fill(f, sentence.length());

        f[0] = 0;
        for (int i = 1; i <= sentence.length(); ++i) {
            f[i] = f[i - 1] + 1;
            long hashValue = 0;
            for (int j = i; j >= 1; --j) {
                int t = sentence.charAt(j - 1) - 'a' + 1;
                hashValue = (hashValue * BASE + t) % P;
                if (hashValues.contains(hashValue)) {
                    f[i] = Math.min(f[i], f[j - 1]);
                }
            }
        }

        return f[sentence.length()];
    }

    public long getHash(String s) {
        long hashValue = 0;
        for (int i = s.length() - 1; i >= 0; --i) {
            hashValue = (hashValue * BASE + s.charAt(i) - 'a' + 1) % P;
        }
        return hashValue;
    }
}


【总结】

1.动态规划流程

第 1 步:设计状态
第 2 步:状态转移方程
第 3 步:考虑初始化
第 4 步:考虑输出
第 5 步:考虑是否可以状态压缩

2. Rabin-Karp 字符串编码 (字符串哈希)

[Leetcode][程序员面试金典][面试题17.13][JAVA][恢复空格][动态规划][Trie][字符串哈希]_第7张图片

3.Trie
class Trie {
    public Trie[] next;
    public boolean isEnd;
    
    public Trie() {
        next = new Trie[26];
        isEnd = false;
    }

    public void insert(String s) {
        Trie curPos = this;

        for (int i = s.length() - 1; i >= 0; --i) {
            int t = s.charAt(i) - 'a';
            if (curPos.next[t] == null) {
                curPos.next[t] = new Trie();
            }
            curPos = curPos.next[t];
        }
        curPos.isEnd = true;
    }
}


转载链接:https://leetcode-cn.com/problems/re-space-lcci/solution/hui-fu-kong-ge-by-leetcode-solution/
Rabin-Karp 字符串编码 参考链接:https://leetcode-cn.com/problems/longest-happy-prefix/solution/zui-chang-kuai-le-qian-zhui-by-leetcode-solution/

你可能感兴趣的:(刷题,java,程序员面试金典,java,动态规划,字符串哈希,Trie)