最近忙于上课、健身,也不愿抽出时间来做题了。。是懒了许多。
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;
}
LintCode Word Search
题目描述
给定一个字符串矩阵,从里面找出给定单词,可以横着组,竖着组都可以。
思路
经典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];
}