LintCode 解体记录 17.9.26

最近忙于上课、健身,也不愿抽出时间来做题了。。是懒了许多。

LintCode Add Digits

题目描述

给定一个非负的整数,重复地把该数所有位上的数字加起来,直到只有一个数字。

挑战

Could you do it without any loop/recursion in O(1) runtime?

思路

利用模10除10求得整数上的每一位之后,相加,循环处理直到得到的数小于10.
至于挑战的思路,是看网上别人写的,意思就是你从1开始列举一下最后的结果,发现是1-9为一循环,即当n不能整除9时答案就是n,能整除9时答案就是9。综合一下可以写成(n-1)%9 + 1。

代码

贴一个自己写的第一种方法。

    int addDigits(int num) {
        // write your code here
        while (num >= 10) {
            int tmp = 0;
            while (num) {
                tmp += num % 10;
                num /= 10;
            }
            num = tmp;
        }
        return num;
    }

LintCode Trailling Zeros

题目描述

计算n的阶乘的末尾0的个数,n的取值范围为long long。

挑战

O(log N) time

思路

由于n的阶乘是一个非常大非常大的数,所以先把n的阶乘计算出来肯定是不现实的。发现计算末尾0的个数就是计算能整除几个10,又因为10=2x5,且显然阶乘因子中2的出现频率是肯定比5要高的多的,所以就是所如果能整除5,那么他就一定能整除10。比如n=29,那么发现它的阶乘因子里是5的倍数的有5,10,15,20,25有5个。由于要考虑类似25这样的有多个5的因子,所以把n/5,得n=5,这里5的倍数的因子只有一个5,所以一共有6个5的倍数因子,即末尾有6个0。

代码
    long long trailingZeros(long long n) {
        // write your code here, try to do it without arithmetic operators.
        long long res = 0;
        while (n) {
            res += n/5;
            n /= 5;
        }
        return res;
    }

LintCode Perfect Squares

题目描述

给定一个正数n,可以把它写成k个完全平方数的和,求最小的k。

思路

可以找到最大的一个完全平方数是sqrt(n)。因此就可以从这个上界开始向下找,找出所有可能的答案中的最小值。

注意

刚开始的dfs发现超时了,加一个剪枝条件,即若发现当前的次数已经比之前得到的答案要大了,那么就直接放弃接下来的查找。

代码
    int numSquares(int n) {
        // write your code here
        int cnt = 0, res = INT_MAX;
        helper(n, cnt, res);
        return res;
    }

    void helper(int n, int cnt, int &res) {
        if (n < 4) {
            res = min(res, cnt+n);
            return;
        }
        int upperBound = sqrt(n);
        for (int i = upperBound; i >= 1; i--) {
            if (cnt+1 >= res) break;
            helper(n-i*i, cnt+1, res);
        }
    }

LintCode Pow(x, n)

题目描述

计算pow(x, n)

挑战

O(logn) time

思路

你直接返回一个pow(x, n)就AC了。。
其实这里考察了分治思想,比如我要计算2^32次方,如果每一次乘以一个2这样来算显然是需要32次,即O(n)的时间复杂度;如果运用分治的思想,即2^32 = 2^16 * 2^16, 2^16 = 2^8 * 2^8,这样的时间复杂度显然就是O(logn)了。

注意

注意这里的n可以为负数。

代码
    double myPow(double x, int n) {
        // Write your code here
        if (n == -1) return 1/x;
        if (n == 1) return x;
        if (n == 0) return 1;
        int e = n >> 1;
        double res1 = myPow(x, e);
        double res2 = myPow(x, n-2*e);
        return res1 * res1 * res2;
    }

LintCode Add Two Numbers

题目描述

有两个链表表示两个整数,其中数字的顺序是倒序的,即最高位的数字在链表的最低位,现在让你把这两个整数加起来,然后按照上述的表示方式返回表示这个和的链表。

思路

简单粗暴,直接遍历这两个链表,每一位相加,把结果存在一个容器里,最后在利用尾插法建立一个新的链表返回即可。

代码
    ListNode * addLists(ListNode * l1, ListNode * l2) {
        // write your code here
        vector<int> v;
        int c = 0;
        ListNode *p1 = l1, *p2 = l2;
        while (p1 && p2) {
            int n = p1->val + p2->val + c;
            v.push_back(n % 10);
            c = n / 10;
            p1 = p1->next;
            p2 = p2->next;
        }

        while (p1) {
            int n = p1->val + c;
            v.push_back(n % 10);
            c = n / 10;
            p1 = p1->next;
        }

        while (p2) {
            int n = p2->val + c;
            v.push_back(n % 10);
            c = n / 10;
            p2 = p2->next;
        }

        if (c) v.push_back(c);

        ListNode *head = new ListNode(0);
        ListNode *tail = head;
        for (auto n: v) {
            ListNode *p = new ListNode(n);
            tail->next = p;
            tail = p;
        }
        return head->next;
    }
LintCode Add Two Numbers II
题目描述

类似于Add Two Numbers,只不过这里数字是按正常顺序存放的。

思路

先遍历一遍把链表所代表的数用string存起来,然后将两个string相加,最后建立新的链表。

代码
    ListNode * addLists2(ListNode * l1, ListNode * l2) {
        // write your code here
        string num1 = "", num2 = "";
        ListNode *p1 = l1, *p2 = l2;
        while (p1) {
            num1 += p1->val + '0';
            p1 = p1->next;
        }
        while (p2) {
            num2 += p2->val + '0';
            p2 = p2->next;
        }
        string num = AddTwoString(num1, num2);

        ListNode *head = new ListNode(0);
        ListNode *tail = head;
        for (auto c: num) {
            ListNode *p = new ListNode(c-'0');
            tail->next = p;
            tail = p;
        }
        return head->next;
    }

    string AddTwoString(string num1, string num2) {
        string ret = "";
        if (num1.size() > num2.size()) swap(num1, num2);
        int i = 0;
        int c = 0;
        for (; i < num1.size(); i++) {
            int n = num1[num1.size()-1-i]-'0' + num2[num2.size()-1-i]-'0' + c;
            ret += n % 10 + '0';
            c = n / 10;
        }

        for (; i < num2.size(); i++) {
            int n = num2[num2.size()-1-i] - '0' + c;
            ret += n % 10 + '0';
            c = n / 10;
        }

        if (c) ret += c + '0';
        reverse(ret.begin(), ret.end());
        return ret;
    }
补充

当然这题的解法有很多,比如先遍历一遍链表,把数字压入堆栈,然后弹栈处理的,也有直接反转单链表变成上一题的等等等。。

LintCode Split String

题目描述

给定一个字符串,你可以每一个或者每两个进行分割,返回所有可能的结果

思路

dfs搜索+回溯。

代码
    vector<vector<string>> splitString(string& s) {
        // write your code here
        vector<vector<string>> res;
        vector<string> tmp;
        dfs(s, 0, tmp, res);
        return res;
    }

    void dfs(string& s, int idx, vector<string> tmp, vector<vector<string>> &res) {
        if (idx >= s.size()) {
            if (idx == s.size()) res.push_back(tmp);
            return;
        }
        for (int i = 1; i <= 2; i++) {
            tmp.push_back(s.substr(idx, i));
            dfs(s, idx+i, tmp, res);
            tmp.pop_back();
        }
    }

LintCode Find the Missing NumberII

题目描述

给定一个字符串,其由1-n内的所有数随机组合,但是其中少一个数,请找出这个数。

思路

dfs搜索加回溯。首先利用等差数列求和公式求得所有的和,然后每当搜索到一个数时就减掉该数,最后剩下的就是该数。

注意

有可能出现最后还剩两个数,那么此时就显然不是答案,所以终止条件时要判断该数是否出现过。

代码
    int findMissing2(int n, string &str) {
        // write your code here
        vector<int> hash(31, 0);
        int sum = (1+n) * n / 2;
        int res = 0;
        dfs(hash, sum, n, str, 0, res);
        return res;
    }

    void dfs(vector<int> hash, int sum, int n, string &str, int idx, int &res) {
        if (idx >= str.size()) {
            if (idx == str.size() && hash[sum] == 0) res = sum;
            return;
        }
        for (int i = 1; i <= 2; i++) {
            int num = atoi(str.substr(idx, i).c_str());
            if (str[idx] == '0' || num > n) break;
            if (hash[num] == 1) continue;
            sum -= num;
            hash[num] = 1;
            dfs(hash, sum, n, str, idx+i, res);
            sum += num;
            hash[num] = 0;
        }
    }

LintCode Palindrome Partitioning

题目描述

给定一个字符串,将其分割成若干子字符串且这些子字符串都是回文串。返回所有可能的答案。

思路

dfs+回溯。

代码
    bool isPalindrome(string s) {
        string tmp = s;
        reverse(tmp.begin(), tmp.end());
        return s == tmp;
    }
    vector<vector<string>> partition(string &s) {
        // write your code here
        vector<vector<string>> res;
        vector<string> tmp;
        dfs(res, tmp, 0, s);
        return res;
    }

    void dfs(vector<vector<string>> &res, vector<string> tmp, int idx, string s) {
        if (idx == s.size()) {
            res.push_back(tmp);
            return;
        }
        for (int i = idx; i < s.size(); i++) {
            string str = s.substr(idx, i-idx+1);
            if (isPalindrome(str)) {
                tmp.push_back(str);
                dfs(res, tmp, i+1, s);
                tmp.pop_back();
            }
        }
    }

LintCode Word BreakIII

题目描述

给定一个字符串s,和一个字典dict,将s分割成若干子字符串,这些子字符串都在字典中,返回所有可能的结果的个数。

思路

dfs+回溯

注意

如何遍历set,set的遍历利用迭代器和vector形式上好像是一样的,和map不一样,虽然set和map同属关联容器。
给定dict可以先遍历一遍求其单词的maxLen,然后用这个manLen去剪枝后面的dfs。

代码
    int wordBreak3(string& s, unordered_set<string>& dict) {
        // Write your code here
        int res = 0;
        int maxLen = 0;
        for (auto str: dict) {
            maxLen = max(maxLen, (int)str.size());
        }
        dfs(s, dict, res, 0, maxLen);
        return res;
    }

    void dfs(string &s, unordered_set<string>& dict, int &res, int idx, int maxLen) {
        if (idx == s.size()) {
            res++;
            return;
        }
        for (int i = idx; i < s.size(); i++) {
            string word = s.substr(idx, i-idx+1);
            if (word.size() > maxLen) break;
            if (dict.find(word) != dict.end()) {
                dfs(s, dict, res, i+1, maxLen);
            }
        }
    }

LintCode Expression Expand

题目描述

直接看几个例子比较直接:
s = abc3[a] return abcaaa
s = 3[abc] return abcabcabc
s = 4[ac]dy, return acacacacdy
s = 3[2[ad]3[pf]]xyz, return adadpfpfpfadadpfpfpfadadpfpfpfxyz

思路

第一眼看到这种,秒想到了堆栈。然后就用堆栈来做。一个为数字栈,一个为字符串栈。遍历到右括号时开始进行处理。

注意

num可能为多位数。

代码
string expressionExpand(string &s) {
        // write your code here
        string res = "";
        stack<char> alpha;
        stack<int> num;
        int i = 0;

        while (i < s.size()) {
            int cnt = 0;
            bool digit = false;
            while (isdigit(s[i])) {
                digit = true;
                cnt = 10*cnt + s[i]-'0';
                i++;
            }
            if (digit) num.push(cnt); //避免压入不存在的数字0.
            if (isalpha(s[i]) || s[i] == '[') {
                alpha.push(s[i]);
                i++;
            } else if (s[i] == ']') {
                string tmp = "";
                while (alpha.top() != '[') {
                    tmp += alpha.top();
                    alpha.pop();
                }
                alpha.pop();
                reverse(tmp.begin(), tmp.end()); //首先把栈里的字符串全部弹出来,直到遇见左括号

                int c = num.top(); num.pop();
                string temp = "";
                while (c--) {
                    temp += tmp;
                } //然后从数字栈弹出一个数,把这个字符串重复好多次。
                for (auto c: temp) alpha.push(c);//最后把这个新的字符串再次压入字符串栈中。
                i++;
            }

        }
        while (!alpha.empty()) {
            res += alpha.top();
            alpha.pop();
        }
        reverse(res.begin(), res.end());
        return res;

    }
题目描述

给定一个字符串矩阵,从里面找出给定单词,可以横着组,竖着组都可以。

思路

经典DFS。时间复杂度的话额不是很懂。。

代码
    bool exist(vector<vector<char>> &board, string &word) {
        // write your code here
        if (board.size() == 0 || board[0].size() == 0) return false;
        int m = board.size(), n = board[0].size();
        vector<vector<int>> vis(m, vector<int>(n, 0));
        bool res = false;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == word[0]) {
                    dfs(board, i, j, word, 1, vis, m, n, res);
                }
                if (res) return res;
            }
        }
        return res;

    }

    void dfs(vector<vector<char>> &board, int x, int y, string &word, int idx,
        vector<vector<int>> vis, int m, int n, bool &find) {
        if (idx == word.size()) {
            find = true;
            return;
        }
        int dx[] = {1, 0, -1, 0};
        int dy[] = {0, 1, 0, -1};
        //end 
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
            if (vis[nx][ny] == 1) continue;
            vis[nx][ny] = 1;
            if (board[nx][ny] == word[idx]) {
                dfs(board, nx, ny, word, idx+1, vis, m, n, find);
            }
            if (find) return;
            vis[nx][ny] = 0;
        }
    }

LintCode Word BreakII

题目描述

给一字串s和单词的字典dict,在字串中增加空格来构建一个句子,并且所有单词都来自字典。
返回所有有可能的句子。

思路

还是dfs+回溯,只是细节地方上要进行处理。另外这个方法是无法AC的,因为有一个测试用例卡住了,需要先用Word Break的方法判断一下能否进行word Break,然后再调用本题的dfs。

代码
    vector<string> wordBreak(string &s, unordered_set<string> &wordDict) {
        // write your code here
        vector<string> res;
        if (!isWordBreak(s, wordDict)) return res;
        string tmp = "";
        int maxLen = 0;
        for (auto str : wordDict) {
            if (str.size() > maxLen)
                maxLen = str.size();
        }
        dfs(s, wordDict, res, 0, tmp, maxLen);
        return res;
    }
    void dfs(string &s, unordered_set<string> &wordDict, vector<string> &res,
        int idx, string tmp, int maxLen) {
        if (idx == s.size()) {
            res.push_back(tmp);
            return;
        }
        for (int i = idx; i < s.size() && i-idx+1 <= maxLen; i++) {
            string word = s.substr(idx, i-idx+1);

            if (wordDict.find(word) == wordDict.end()) continue;
            string origin = tmp;
            tmp += (i+1) == s.size() ? word : word + " ";
            dfs(s, wordDict, res, i+1, tmp, maxLen);
            tmp = origin;
        }
    }

    int isWordBreak(string &s, unordered_set<string> &wordDict) {
        int n = s.size();
        vector<int> dp(n+1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = i-1; j >= 0; j--) {
                if (dp[j] && wordDict.find(s.substr(j, i-j)) != wordDict.end()) {
                    dp[i] = 1;
                    break;
                }
            }
        }
        return dp[n];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值