Showing posts with label KMP. Show all posts
Showing posts with label KMP. Show all posts

Friday, October 26, 2018

686. Repeated String Match

686Repeated String Match
Given two strings A and B, find the minimum number of times A has to be repeated such that B is a substring of it. If no such solution, return -1.
For example, with A = "abcd" and B = "cdabcdab".
Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring of it; and B is not a substring of A repeated two times ("abcdabcd").
Note:
The length of A and B will be between 1 and 10000.
--------------------
为满足包含B的条件,A最多重复的次数不会超过B.length() + A.length() * 2

Solution #1, 暴力解
注意返回时的count的计算
O(m * n)
class Solution {
    public int repeatedStringMatch(String A, String B) {
        String s = A;
        while (s.length() <= B.length() + A.length()) {
            s += A;
        }
        
        for (int i = 0; i < s.length() - B.length() + 1; i++) {
            String sub = s.substring(i, i + B.length());
            if (sub.equals(B)) {
                return (i + B.length() + A.length() - 1) / A.length();
            }
        }
    
        return -1;
    }
}

Solution #2, 典型的KMP
理解的要点
1. 找每个点的longest proper preffix == longest proper suffix (proper的意思是string本身不能算*ffix)
2. preprocess里是pattern自己比自己。主方法里是pattern对比A。2者的原理其实是一样的。
3. table里存的是proper的长度

class Solution {
    public int repeatedStringMatch(String A, String pattern) {
        int i = 0, j = 0;
        int[] table = preprocess(pattern);
        
        String s = A;
        while (s.length() < pattern.length() + A.length()) {
            s += A;
        }
        
        while (i < s.length()) {
            if (s.charAt(i) == pattern.charAt(j)) {
                j++;
                i++;
                if (j == pattern.length()) break;
            }else if (j > 0) {
                j = table[j - 1];
            }else {
                i++;
            }
        }
        System.out.println(i);
        if (i == s.length()) return -1;
        return (i + A.length() - 1) / A.length();
    }
    
    private int[] preprocess(String s) {
        int[] table = new int[s.length()];
        int j = 0, i = 1;

        while (i < s.length()) {
            if (s.charAt(j) == s.charAt(i)) {
                table[i] = j + 1;
                j++;
                i++;
            }else if (j > 0) {
                j = table[j - 1];
            }else {
                i++;
            }
        }
        
        return table;
    }
}

Tuesday, June 16, 2015

Day 108, ##,Add and Search Word - Data structure design, House Robber II, Shortest Palindrome

Add and Search Word - Data structure design
Design a data structure that supports the following two operations:
void addWord(word)
bool search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.
For example:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true
Note:
You may assume that all words are consist of lowercase letters a-z.
--------------------------------------------------------------
Trie,
class TrieNode {
public:
    vector<TrieNode *> children;
    bool end;
    TrieNode() {
        end = false;
        children = vector<TrieNode *>(26,NULL);
    }
};

class WordDictionary {
public:
    WordDictionary() {
        root = new TrieNode();
    }
    
    // Adds a word into the data structure.
    void addWord(string word) {
        TrieNode *itr = root;
        for (int i = 0; i < word.length(); i++) {
            if (itr->children[word[i] - 'a'] == NULL) {
                itr->children[word[i] - 'a'] = new TrieNode();
            }
            itr = itr->children[word[i] - 'a'];
        }
        itr->end = true;
    }

    bool searchHelper(string word, int index,TrieNode *itr) {
        if (index == word.length()) return itr->end;
        if (word[index] != '.') {
            TrieNode* t = itr->children[word[index] - 'a']; 
            if (t != NULL) {
                return searchHelper(word,index + 1, t);
            }
            return false;
        }
        
        for (int i = 0; i < 26; i++) {
            TrieNode* t = itr->children[i]; 
            if (t != NULL && searchHelper(word,index + 1, t)) {
                return true;
            }
        }
        
        return false;
    }

    // Returns if the word is in the data structure. A word could
    // contain the dot character '.' to represent any one letter.
    bool search(string word) {
        return searchHelper(word,0,root);
    }
private:
    TrieNode* root;
};

// Your WordDictionary object will be instantiated and called as such:
// WordDictionary wordDictionary;
// wordDictionary.addWord("word");
// wordDictionary.search("pattern");


Java, addWord可以用for循环,也可以递归。searchWord的时候只能递归了,因为遇到‘.’得尝试所有26位,然后backtrace
class WordDictionary {
    
    private Node root;

    /** Initialize your data structure here. */
    public WordDictionary() {
        root = new Node('0');
    }
    
    /** Adds a word into the data structure. */
    public void addWord(String word) {
        
        Node itr = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!itr.letters.containsKey(c)){
                itr.letters.put(c, new Node(c));
            }
            
            if (i == word.length() - 1) {
                itr.letters.get(c).isWord = true;
            }
            
            itr = itr.letters.get(c);
        }
        
    }
    
    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    public boolean search(String word) {
        
        return helper(word, 0, root);
    }
    
    private boolean helper(String word, int index, Node node) {
        
        if (index == word.length()) {
            return node.isWord;
        }
        
        char c = word.charAt(index);
        if (node.letters.containsKey(c)) {
            if (helper(word, index + 1, node.letters.get(c))) return true;
        }else if (c == '.') {
            for (Map.Entry entry : node.letters.entrySet()) {
                if (helper(word, index + 1, entry.getValue())) {
                    return true;
                }
            }
        }
        
        return false;
        
    }
}

class Node{
    public char c;
    public Map letters;
    public boolean isWord;
    
    public Node(char c) {
        this.c = c;
        letters = new HashMap();
        isWord = false;
    }
}

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary obj = new WordDictionary();
 * obj.addWord(word);
 * boolean param_2 = obj.search(word);
 */
Word Search II
Given a 2D board and a list of words from the dictionary, find all words in the board.
Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.
For example,
Given words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
Return ["eat","oath"].
Note:
You may assume that all inputs are consist of lowercase letters a-z.
You would need to optimize your backtracking to pass the larger test. Could you stop backtracking earlier?
If the current candidate does not exist in all words' prefix, you could stop backtracking immediately. What kind of data structure could answer such query efficiently? Does a hash table work? Why or why not? How about a Trie? If you would like to learn how to implement a basic trie, please work on this problem: Implement Trie (Prefix Tree) first.
----------------------------------------------------
Used prefix tree from the previous question
class TrieNode {
public:
    TrieNode() {
        next = vector<TrieNode*>(26,NULL);
        terminal = false;
    }
 
    char value;
    vector<TrieNode*> next;
    bool terminal;
};

class Trie {
public:
    Trie() {
        root = new TrieNode();
    }
 
    // Inserts a word into the trie.
    void insert(string s) {
        TrieNode *cur = root;
        for (int i = 0; i < s.length(); i++) {
            char c = s[i] - 'a';
            if (cur->next[c] == NULL) {
                TrieNode *trie = new TrieNode();
                trie->value = c;
                cur->next[c] = trie;
            }
            cur = cur->next[c];
        }
        cur->terminal = true;
    }
 
    // Returns if the word is in the trie.
    bool search(string key) {
        TrieNode *cur = root;
        for (int i = 0; i < key.length(); i++) {
            char c = key[i] - 'a';
            if (cur->next[c] == NULL) {
                return false;
            }
             
            cur = cur->next[c];
        }
         
        return cur->terminal;
    }
 
    // Returns if there is any word in the trie
    // that starts with the given prefix.
    bool startsWith(string prefix) {
        TrieNode *cur = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix[i] - 'a';
            if (cur->next[c] == NULL) {
                return false;
            }
             
            cur = cur->next[c];
        }
         
        return true;
    }
private:
    TrieNode* root;
};

class Solution {
public:
    void search(vector<string> &rt, vector<vector<char>>& board, 
            vector<vector<bool> > &visit, Trie &trie, string word, int row, int col, unordered_set<string> &hadIt) {
        if (row < 0 || row >= board.size() || col < 0 || col >= board[0].size() || visit[row][col]) {
            return;
        }
        word += board[row][col];
        visit[row][col] = true;
        if (hadIt.find(word) == hadIt.end() && trie.search(word)) {
            hadIt.insert(word);
            rt.push_back(word);
        }
        
        if (trie.startsWith(word)) {
            search(rt,board,visit,trie,word,row + 1,col,hadIt);
            search(rt,board,visit,trie,word,row - 1,col,hadIt);
            search(rt,board,visit,trie,word,row,col + 1,hadIt);
            search(rt,board,visit,trie,word,row,col - 1,hadIt);
        }
        visit[row][col] = false;
    }

    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        Trie trie;
        for (int i = 0; i < words.size(); i++) {
            trie.insert(words[i]);
        }
        
        vector<string> rt;
        vector<vector<bool> > visit(board.size(),vector<bool>(board[0].size(),false));
        unordered_set<string> hadIt;
        for (int i = 0; i < board.size(); i++) {
            for (int j = 0; j <board[0].size(); j++) {
                search(rt,board,visit,trie,"",i,j,hadIt);
            }
        }
        
        return rt;
    }
};

House Robber II
Note: This is an extension of House Robber.
After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
----------------------------------------------
看了答案!
class Solution {
public:
    int robHelper(vector<int>& nums, int left, int right) {
        int pre_1 = 0, pre_2 = 0, pre_3 = 0;
        for (int i = left; i <= right; i++) {
            int temp = pre_3;
            pre_3 = max(pre_3,max(pre_1,pre_2) + nums[i]);
            pre_1 = pre_2;
            pre_2 = temp;
        }
        
        return pre_3;
    }

    int rob(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return nums[0];
        return max(robHelper(nums,0,nums.size() - 2),robHelper(nums,1,nums.size() - 1));
    }
};

Shortest Palindrome
Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.
For example:
Given "aacecaaa", return "aaacecaaa".
Given "abcd", return "dcbabcd".
---------------------------------------------------------------
O(n^2) 找prefix是palindrome。超时
class Solution {
public:
    bool isPalindrome(string s) {
        for (int i = 0; i < s.length() / 2; i++) {
            if (s[i] != s[s.length() - 1 - i]) return false;
        }
        
        return true;
    }
    
    int longestPal(string s) {
        for (int i = s.length(); i > 0; i--) {
            if (isPalindrome(s.substr(0,i))) {
                return i;
            }
        }
        
        return 0;
    }
    
    string shortestPalindrome(string s) {
        int length = longestPal(s);
        string copy = s;
        for (int i = 0; i < copy.length() - length; i++) {
            s = copy[length + i] + s;    
        }
        
        return s;
    }
};
看了答案!对KMP要灵活运用
s + special char + reverse(s)
然后运用KMP的failure function计算出s的prefix和revsers(s)的suffix相等的最长长度
ref: https://leetcode.com/discuss/36807/c-8-ms-kmp-based-o-n-time-%26-o-n-memory-solution
class Solution {
public:
    vector<int> computePrefixTable(string pattern) {
        int m = pattern.length();
        vector<int> table(m,0);
        int matchedLength = 0;
        
        for (int i = 1; i < m; i++) {
            // until find the next char at matchedLength is equal to char at i
            // or matchedLength is zero
            while (matchedLength > 0 && pattern[matchedLength] != pattern[i]) {
                matchedLength = table[matchedLength - 1];
            }
            if (pattern[matchedLength] == pattern[i]) {
                matchedLength++;
            }
            table[i] = matchedLength;
        }
        
        return table;
    }
    string shortestPalindrome(string s) {
        string rev = s;
        reverse(rev.begin(),rev.end());
        string pattern = s + "*" + rev;
        vector<int> table = computePrefixTable(pattern);
        
        return rev.substr(0,rev.length() - table[table.size() - 1]) + s;
    }
};

Friday, November 22, 2013

Day 53, 28, Implement strStr()

Implement strStr()
Implement strStr().
Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.
------------------------------------------------- 
KMP, O(n),
class Solution {
public:
    vector<int> computePrefixTable(string pattern) {
        int m = pattern.length();
        vector<int> table(m,0);
        int matchedLength = 0;
        
        for (int i = 1; i < m; i++) {
            // until find the next char at matchedLength is equal to char at i
            // or matchedLength is zero
            while (matchedLength > 0 && pattern[matchedLength] != pattern[i]) {
                matchedLength = table[matchedLength - 1];
            }
            if (pattern[matchedLength] == pattern[i]) {
                matchedLength++;
            }
            table[i] = matchedLength;
        }
        
        return table;
    }
    
    int KMP(string source, string pattern) {
        int n = source.length();
        int m = pattern.length();
        vector<int> table = computePrefixTable(pattern);
        int matchedLength = 0;
        
        for (int i = 0; i < n; i++) {
            while (matchedLength > 0 && pattern[matchedLength] != source[i]) {
                matchedLength = table[matchedLength - 1];
            }
            
            if (pattern[matchedLength] == source[i]) {
                matchedLength++;
            }
            if (matchedLength == m) {
                return i - m + 1;
            }
        }
        
        return -1;
    }

    int strStr(string haystack, string needle) {
        if (needle == "") return 0;
        if (haystack == "") return -1;
        return KMP(haystack,needle);
    }
};
Solution #2 O(m*n), improved brute force
class Solution {
public:
    char *strStr(char *haystack, char *needle) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        if (*needle == NULL) return haystack;
        if (!*haystack) return NULL;
        char* adv = haystack;
        char* temp = needle;
        while (*++temp) {
            adv++;
        }
        
        while (*adv != NULL) {
            char* itr1 = haystack;
            char* itr2 = needle;
            while (*itr1 && *itr2 && *itr1 == *itr2) {
                itr1++;
                itr2++;
            }
            if (!*itr2) return haystack;
            haystack++;
            adv++;
        }
        return NULL;
    }
};