Showing posts with label game theory. Show all posts
Showing posts with label game theory. Show all posts

Tuesday, October 16, 2018

843. Guess the Word

843Guess the Word
This problem is an interactive problem new to the LeetCode platform.
We are given a word list of unique words, each word is 6 letters long, and one word in this list is chosen as secret.
You may call master.guess(word) to guess a word.  The guessed word should have type string and must be from the original list with 6 lowercase letters.
This function returns an integer type, representing the number of exact matches (value and position) of your guess to the secret word.  Also, if your guess is not in the given wordlist, it will return -1 instead.
For each test case, you have 10 guesses to guess the word. At the end of any number of calls, if you have made 10 or less calls to master.guess and at least one of these guesses was the secret, you pass the testcase.
Besides the example test case below, there will be 5 additional test cases, each with 100 words in the word list.  The letters of each word in those testcases were chosen independently at random from 'a' to 'z', such that every word in the given word lists is unique.
Example 1:
Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"]

Explanation:

master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist.
master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 matches.
master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches.
master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches.
master.guess("abcczz") returns 4, because "abcczz" has 4 matches.

We made 5 calls to master.guess and one of them was the secret, so we pass the test case.
Note:  Any solutions that attempt to circumvent the judge will result in disqualification.
-------------------------
扯淡题

思路是用MiniMax, 主要是求“稳”。有多种实现方式。
以下方法是,对每一个词,找出与他匹配数量为0的词的个数,然后用数量最小的那个词来猜
这里的解释不错: http://blog.vrqq.org/archives/363/

check zero match的原因:
“Nice Solution. Anyone who doesn't know why checking 0 match instead of 1,2,3...6 matches, please take a look at this comment. The probability of two words with 0 match is (25/26)^6 = 80%. That is to say, for a candidate word, we have 80% chance to see 0 match with the secret word. In this case, we had 80% chance to eliminate the candidate word and its "family" words which have at least 1 match. Additionally, in order to delete a max part of words, we select a candidate who has a big "family" (fewest 0 match).”
https://leetcode.com/problems/guess-the-word/discuss/133862/Random-Guess-and-Minimax-Guess-with-Comparison
通常情况下,猜一个词,返回为0的概率胃79% (25/26 ^ 6),这种情况下,wordlist范围缩小的力度也是最小的(因为任意2个词的match为0的几率高达79%)。我们要增大缩小的力度,减小match为0时新生成的list的大小。所以我们选出“找出与他匹配数量为0的词的个数,然后用数量最小的那个词来猜”


/**
 * // This is the Master's API interface.
 * // You should not implement it, or speculate about its implementation
 * interface Master {
 *     public int guess(String word) {}
 * }
 */
class Solution {
    public void findSecretWord(String[] wordList, Master master) {
        List<String> list = Arrays.asList(wordList);
        
        while (!list.isEmpty()) {
            Map<String, Integer> zeroMatch = new HashMap<>();
            for (String cand : list) {
                for (String word : list) {
                    if (match(cand, word) == 0) zeroMatch.put(cand, zeroMatch.getOrDefault(cand, 0) + 1);
                }
            }    
            
            String guess = list.get(0);
            int min = 110;
            for (Map.Entry<String, Integer> entry : zeroMatch.entrySet()) {
                if (min > entry.getValue()) {
                    min = entry.getValue();
                    guess = entry.getKey();
                }
            }
            
            int m = master.guess(guess);
            if (m == 6) return;
            
            List<String> tmp = new ArrayList<>();
            for (String s : list) {
                if (m == match(s, guess)) {
                    tmp.add(s);    
                }
            }
            
            list = tmp;
        }
    }
    
    private int match(String a, String b) {
        int m = 0;
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) == b.charAt(i)) m++;
        }
        
        return m;
    }
}

另一种实现方式,LC官方解答:https://leetcode.com/problems/guess-the-word/solution/

Tuesday, October 27, 2015

Day 131, #288 #289 #293 #294 #295 #298 Unique Word Abbreviation, Game of Life, Flip Game, Flip Game II, Find Median from Data Stream, Binary Tree Longest Consecutive Sequence

Unique Word Abbreviation
An abbreviation of a word follows the form <first letter><number><last letter>. Below are some examples of word abbreviations:
a) it                      --> it    (no abbreviation)

     1
b) d|o|g                   --> d1g

              1    1  1
     1---5----0----5--8
c) i|nternationalizatio|n  --> i18n

              1
     1---5----0
d) l|ocalizatio|n          --> l10n
Assume you have a dictionary and given a word, find whether its abbreviation is unique in the dictionary. A word's abbreviation is unique if no other word from the dictionary has the same abbreviation.
Example: 
Given dictionary = [ "deer", "door", "cake", "card" ]

isUnique("dear") -> false
isUnique("cart") -> true
isUnique("cane") -> false
isUnique("make") -> true
-----------------------------------------------------------------
COME_BACK
看清题意
class ValidWordAbbr {
public:
    string getAbre(string s) {
        int len = s.length();
        if (len > 1) {
            s = s[0] + to_string(len - 2) + s[len - 1];
        }
        return s;
    }
    
    ValidWordAbbr(vector<string> &dictionary) {
        for (int i = 0; i < dictionary.size(); i++) {
            string abre = getAbre(dictionary[i]);
            if (dic.find(abre) == dic.end()) {
                vector<string> second;
                second.push_back(dictionary[i]);
                dic[abre] = second;
            }else {
                dic[abre].push_back(dictionary[i]);
            }
        }
    }

    bool isUnique(string word) {
        string abre = getAbre(word);
        if (dic.find(abre) == dic.end()) return true;
        return dic[abre][0] == word && dic[abre].size() == 1;
    }
    
private:
    unordered_map<string,vector<string>> dic;
};


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

Game of Life
According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."
Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):
  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population..
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
Write a function to compute the next state (after one update) of the board given its current state.
Follow up
  1. Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
  2. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?
---------------------------------------------------------------------
额外空间
class Solution {
public:
    vector<int> row;
    vector<int> col; 
    bool isLive(int i, int j, int m, int n, vector<vector<int>>& board) {
        if (i < 0 || j < 0 || i == m || j == n) return false;
        return board[i][j];
    }

    void gameOfLife(vector<vector<int>>& board) {
        int m = board.size();
        int n = board[0].size();
        vector<vector<int>> rt(m, vector<int>(n));
        row = {-1,-1,-1,0,0,1,1,1};
        col = {-1,0,1,-1,1,-1,0,1};
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int numOfLives = 0;
                for (int k = 0; k < 8; k++) {
                    if (isLive(row[k] + i,col[k] + j, m, n, board)) {
                        numOfLives++;
                    }
                }
                
                if (numOfLives < 2 || numOfLives > 3) rt[i][j] = 0;
                else if (board[i][j] && numOfLives <= 3) rt[i][j] = 1;
                else if (numOfLives == 3) rt[i][j] = 1;
            }
        }
        board = rt;
    }
};

constant space
用-1来表示之前为0,现在为1. -2来表示之前为1,现在为0
0 -> -1 -> 1
1 -> -2 -> 0
class Solution {
public:
    vector<int> row;
    vector<int> col; 
    bool isLive(int i, int j, int m, int n, vector<vector<int>>& board) {
        if (i < 0 || j < 0 || i == m || j == n) return false;
        if (board[i][j] == 0 || board[i][j] == -1) return false; 
        return true;
    }

    void gameOfLife(vector<vector<int>>& board) {
        int m = board.size();
        int n = board[0].size();
        row = {-1,-1,-1,0,0,1,1,1};
        col = {-1,0,1,-1,1,-1,0,1};
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int numOfLives = 0;
                for (int k = 0; k < 8; k++) {
                    if (isLive(row[k] + i,col[k] + j, m, n, board)) {
                        numOfLives++;
                    }
                }
                
                if (board[i][j] && numOfLives < 2) board[i][j] = -2;
                else if (board[i][j] && (numOfLives == 3 || numOfLives == 2)) continue;
                else if (board[i][j] && numOfLives > 3) board[i][j] = -2;
                else if (numOfLives == 3) board[i][j] = -1;
            }
        }
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == -1) board[i][j] = 1;
                if (board[i][j] == -2) board[i][j] = 0;
            }
        }
    }
};

Flip Game
You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip twoconsecutive "++" into "--". The game ends when a person can no longer make a move and therefore the other person will be the winner.
Write a function to compute all possible states of the string after one valid move.
For example, given s = "++++", after one move, it may become one of the following states:
[
  "--++",
  "+--+",
  "++--"
]
If there is no valid move, return an empty list [].
---------------------------------------------
class Solution {
public:
    vector<string> generatePossibleNextMoves(string s) {
        vector<string> rt;
        for (int i = 1; i < s.length(); i++) {
            if (s[i] == '+' && s[i - 1] == '+') {
                s[i] = '-';
                s[i - 1] = '-';
                rt.push_back(s);
                s[i] = '+';
                s[i - 1] = '+';
            }
        }
        
        return rt;
    }
};

Flip Game II
You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip twoconsecutive "++" into "--". The game ends when a person can no longer make a move and therefore the other person will be the winner.
Write a function to determine if the starting player can guarantee a win.
For example, given s = "++++", return true. The starting player can guarantee a win by flipping the middle "++" to become "+--+".
Follow up:
Derive your algorithm's runtime complexity.
--------------------------------------------------------------
递归
T(n) = n * T(n - 1)
        = n * (n - 1) * T(n - 2)
        = n * (n - 1) * (n - 2) * T(n - 3)
        ...
        = n!
class Solution {
public:
    bool canWin(string s) {
        for (int i = 1; i < s.length(); i++) {
            if (s[i] == '+' && s[i - 1] == '+') {
                s[i] = '-';
                s[i - 1] = '-';
                if (!canWin(s)) return true;
                s[i] = '+';
                s[i - 1] = '+';
            }
        }
        
        return false;
    }
};
COME_BACK
game theory的方法可以达到O(n^2)

Find Median from Data Stream
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
Examples: 
[2,3,4] , the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
  • void addNum(int num) - Add a integer number from the data stream to the data structure.
  • double findMedian() - Return the median of all elements so far.
For example:
add(1)
add(2)
findMedian() -> 1.5
add(3) 
findMedian() -> 2
---------------------------------------------------------------------------
2个queue,还有其他方法吗
一个TreeSet加2个指针,跟2个heap原理是一样的
class MedianFinder {
public:
    struct cmp {
        bool operator() (int i1, int i2) {
            return i1 > i2;
        }
    };
    
    // Adds a number into the data structure.
    void addNum(int num) {
        small.push(num);
        int temp = small.top();
        small.pop();
        large.push(temp);
        temp = large.top();
        large.pop();
        if (small.size() > large.size()) {
            large.push(temp);
        }else {
            small.push(temp);
        }
    }

    // Returns the median of current data stream
    double findMedian() {
        if ((small.size() + large.size()) % 2 == 0) {
            return (small.top() + large.top()) / 2.0;
        }else {
            return small.top();
        }
    }

private:
    priority_queue<int> small;
    priority_queue<int,vector<int>,cmp> large;
};

// Your MedianFinder object will be instantiated and called as such:
// MedianFinder mf;
// mf.addNum(1);
// mf.findMedian();

Binary Tree Longest Consecutive Sequence
Given a binary tree, find the length of the longest consecutive sequence path.
The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from parent to child (cannot be the reverse).
For example,
   1
    \
     3
    / \
   2   4
        \
         5
Longest consecutive sequence path is 3-4-5, so return 3.
   2
    \
     3
    / 
   2    
  / 
 1
Longest consecutive sequence path is 2-3,not3-2-1, so return 2.
-------------------------------------------------------
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void dfs(TreeNode* root, int cur, int &maxL, int target) {
        if (root == NULL) return;
        if (root->val == target) {
            cur++;
        }else {
            cur = 1;
        }
        maxL = max(maxL,cur);
        dfs(root->left, cur, maxL, root->val + 1);
        dfs(root->right, cur, maxL, root->val + 1);
    }

    int longestConsecutive(TreeNode* root) {
        int maxL = 0;
        if (root == NULL) return 0;
        dfs(root,0,maxL,root->val);
        return maxL;
    }
};

Saturday, September 5, 2015

Extra: 主要是game theory

来自飞哥
给两个数,每次你都可以用大的数减去小的的数字的正整数倍数(得数必须非负),然后交换玩家,首先得到0的人胜利,问是第一个人还是第二个

假设a,b,当a >= 2b时,当前的玩家获胜
证明:
当 a < 2b时,只有一种情况 (a, b) -> (a - b, b)。(a - b, b)时的获胜者必定会跟(a, b)时的获胜者不为一人(可以用反证法证明这个)
当 a >= 2b时, (a, b)可以转为(a - i * b, b), b < a - i * b <  2b, 所以可以(a, b)可以选择winning position,也可选择loosing position,所以此时(a, b)的玩家必胜

提醒:game theory题的定理:

  • All terminal positions are losing.
  • If a player is able to move to a losing position then he is in a winning position.
  • If a player is able to move only to the winning positions then he is in a losing position.
base case 为当a == b

int canWin(int a, int b, int player) {
    if (a == b) return player;
    if (a < b) return canWin(b,a,player);
    if (a >= 2 * b) return player;
    return canWin(a - b,b,(player + 1) % 2);
}


Thursday, July 2, 2015

Day 114, Lintcode, #395, Coins in a Line II

Coins in a Line II
There are n coins with different value in a line. Two players take turns to take one or two coins from left side until there are no more coins left. The player who take the coins with the most value wins.
Could you please decide the first player will win or lose?
-----------------------------------------------------
方法一
coins表示在i点的最大利益
getTwo表示在i点最大利益是否要取2个coin
class Solution {
public:
    /**
     * @param values: a vector of integers
     * @return: a boolean which equals to true if the first player will win
     */
    bool firstWillWin(vector<int> &values) {
        // write your code here
        int m = values.size();
        if (m < 3) return true;
        
        vector<int> coins(m + 2,0);
        vector<bool> getTwo(m,true);
        
        coins[m - 1] = values[m - 1];
        coins[m - 2] = values[m - 1] + values[m - 2];
        
        for (int i = m - 3; i >= 0; i--) {
            int takeOne = 0, takeTwo = 0;
            // take one
            if (getTwo[i + 1]) {
                takeOne = coins[i + 3] + values[i];
            }else {
                takeOne = coins[i + 2] + values[i];
            }
            
            // take two
            if(getTwo[i + 2]) {
                takeTwo = coins[i + 4] + values[i] + values[i + 1];
            }else {
                takeTwo = coins[i + 3] + values[i] + values[i + 1];
            }
            
            coins[i] = max(takeOne,takeTwo);
            if (takeOne > takeTwo) {
                getTwo[i] = false;
            }
        }
        
        if (getTwo[0]) {
            return coins[0] > coins[2];
        }
        return coins[0] > coins[1];
    }
};

方法二 http://techinpad.blogspot.com/2015/05/lintcode-coins-in-line-ii.html
方法三 http://www.meetqun.com/thread-9798-1-1.html