阿里的笔试过了~~虽说感觉被虐了..但是还是过了..得好好准备面试.过几天就面试,虽然感觉没有准备好,这次就当刷经验了。过不了还有明年3月和4月~
这30题做得时候也有些急,每天都分配了任务,强迫当天完成,总是做到1-2点..所以有些题目代码自己没想出就百度了,不像以前多少还自己写一次,这次百度个思路,看看算法,差不多的改改就交了..由于前一篇有一部分分开存在家里了,所以只好一起发了.
leetcode看来还是按顺序刷好...按AC率来的话,很多难题提交少,AC率比一些简单题还高...最后这31题可谓是难与简单并存....好多题目自己不太懂,就搜的大神的思路,感觉面试45分钟要我写也确实写不出来。。。以后有时间再补起来吧。先就当作读代码的练习了。。。
1.Spiral Matrix
题目意思:按顺时针从矩阵外层到内层,得到一串序列.
说实话这种题目,遇到就只能凑了..做过好多次,还是做得很慢,试错很多次.主要是细节位置太多了.一次性写清楚有点难.
class Solution {
public:
vector spiralOrder(vector > &matrix) {
vector answer;
int row = matrix.size();
if( row == 0) return answer;
int col = matrix[0].size();
if( col == 0) return answer;
int start_x = 0;
int end_x = col - 1;
int start_y = 0;
int end_y = row - 1;
while(start_x <= end_x && start_y <= end_y){
for(int i = start_x ; i <= end_x ;i++){
answer.push_back(matrix[start_y][i]);
}
for(int i = start_y + 1; i <= end_y ;i++){
answer.push_back(matrix[i][end_x]);
}
for(int i = end_x - 1 ; end_y != start_y && i >= start_x ;i--){
answer.push_back(matrix[end_y][i]);
}
for(int i = end_y - 1 ; start_x != end_x && i > start_y ;i--){
answer.push_back(matrix[i][start_x]);
}
start_x++;
start_y++;
end_x--;
end_y--;
}
return answer;
}
};
2.Restore IP Addresses
题目意思:给出一个数字字符串并插入点号,返回所有可能为IP地址的串.
这一题...真蛋疼..开始用resize临时string做的.”0000’我的答案明明和表达一个字符不差..但是就是不过..后来又因为对于010这样的数字是不能当作IP中间的数字的,所以又加了个补充.最后才做出来...看来字符串仍然是我的弱项啊.
class Solution {
public:
vector answer;
string temp;
int size;
vector restoreIpAddresses(string s) {
size = s.size();
DFS(s, 0, 0);
return answer;
}
void DFS(string &s, int level, int cur){
if( level > 4) return ;
if(level == 4){
if( cur == s.size() ) answer.push_back(temp);
return;
}
int val = 0;
for(int i = cur ; i < cur + 3 && i < s.size(); i++){
if(s[i] >= '0' && s[i] <= '9'){
val *= 10;
val += s[i] - '0';
if( val <= 255){
int k;
for(k = cur ; s[k] == '0' && k < i ; k++);
if(k != cur) return;
if(temp.size()) temp.push_back('.');
for(int j = cur ; j <= i ; j++){
temp.push_back(s[j]);
}
DFS(s, level + 1, i + 1);
for(int j = cur ; j <= i ; j++){
temp.pop_back();
}
if(temp.size()) temp.pop_back();
}
}
}
}
};
3.Sort List
题目意思:对一个链表排序,并且要求时间复杂度为O(nlogn)
这一题如果用快排做会超时...我就超了时...本来还觉得很吊的...只好换并归了.......没什么好说的,绝大部分相当于是两个链表合并.
class Solution {
public:
ListNode *sortList(ListNode *head) {
if( head == NULL ) return NULL;
if( head->next == NULL ) return head;
if( head->next->next == NULL){
ListNode *t = head ->next;
if( t->val < head->val){
t->next = head;
head->next = NULL;
head = t;
}
return head;
}
ListNode *fast = head;
ListNode *slow = head;
ListNode *pre = NULL;
while(fast){
fast = fast->next;
if(!fast) break;
fast = fast->next;
pre = slow;
slow = slow->next;
}
pre->next = NULL;
fast = sortList(head);
slow = sortList(slow);
ListNode *tail = NULL;
head = NULL;
while(fast && slow){
if( fast->val < slow->val){
ListNode *pre = fast;
fast = fast->next;
if( !head ) head = tail = pre, tail->next = NULL;
else{
tail->next = pre;
tail = tail->next;
tail->next = NULL;
}
} else {
ListNode *pre = slow;
slow = slow->next;
if( !head ) head = tail = pre, tail->next = NULL;
else{
tail->next = pre;
tail = tail->next;
tail->next = NULL;
}
}
}
while(fast){
ListNode *pre = fast;
fast = fast->next;
if( !head ) head = tail = pre, tail->next = NULL;
else{
tail->next = pre;
tail = tail->next;
tail->next = NULL;
}
}
while(slow){
ListNode *pre = slow;
slow = slow->next;
if( !head ) head = tail = pre, tail->next = NULL;
else{
tail->next = pre;
tail = tail->next;
tail->next = NULL;
}
}
return head;
}
};
4.Reorder List
题目意思:将原链表分别从头和尾开始,交替插入到一个新链表中.
这一题也好奇葩...一反一正..感觉先把后一半的连接给反向.再链表合并即可.感觉主要还是链表找中间结点要适当.对于长度为奇数的链表,后半部分要指向中间结点的后一个!
class Solution {
public:
ListNode *right;
void reorderList(ListNode *head) {
if( head == NULL ) return;
if( head->next == NULL || head->next->next == NULL ) return;
int maxlevel = 0;
ListNode *cur = head;
ListNode *mid = head;
ListNode *pre;
while(cur){
maxlevel ++;
pre = mid;
mid = mid->next;
cur = cur->next;
if(!cur) break;
cur = cur->next;
}
pre->next = NULL;
relink(mid, NULL);
mid = right;
ListNode *tail = NULL;
while(mid){
pre = mid;
mid = mid->next;
pre->next = head->next;
head->next = pre;
head = head->next->next;
}
}
void relink(ListNode *node, ListNode *pre){
if(node == NULL) return ;
if(node->next == NULL){
right = node;
node->next = pre;
return;
}
relink(node->next, node);
node->next = pre;
}
};
5.Binary Tree Maximum Path Sum
题目意思:从二叉树的任意一个结点到任意另外一个节点的路径和的最大值.
刚开始看错了.以为是从叶子结点到叶子结点,结果是任意节点...并且还有可能只是单独的一个节点.思路就是算出节点的左右路径长度.然后根据长度来找到最大的长度.我就直接把答案放在全局变量中了.
版本一:
class Solution {
public:
int max;
int maxPathSum(TreeNode *root) {
max = 0x80000000;
DFS(root);
return max;
}
int DFS(TreeNode *root){
if( root == NULL ) return 0;
if(root->val > max) max = root->val;
int left = DFS(root->left);
int right = DFS(root->right);
int sum = Max(left + right + root->val, Max(left + root->val, right + root->val));
if( sum > max) max = sum;
return Max( root->val, Max(left + root->val, right + root->val));
}
inline int Max(int a,int b){
return a > b ? a : b;
}
};
版本二:
class Solution {
public:
int max;
int maxPathSum(TreeNode *root) {
max = 0x80000000;
DFS(root);
return max;
}
int DFS(TreeNode *root){
if( root == NULL ) return 0;
int left = DFS(root->left);
int right = DFS(root->right);
int sum = Max(left,right);
if(sum < 0) sum = root->val;
else sum += root->val;
if( sum > max) max = sum;
int temp = Max(left + right + root->val , sum);
if( temp > max ) max = temp;
return sum;
}
inline int Max(int a,int b){
return a > b ? a : b;
}
};
感觉版本二更简洁些,但实际上版本一要快30+ms.
-6.Regular Expression Matching
题目意思:判断一个正则表达式是否匹配.只匹配点号和星号.
isMatch("aab", "c*a*b") → true这一句一开始没明白什么意思.其实是c可以有*个,a有*个,b有一个.这题应该属于编译原理有穷自动机,个人学得不怎样...所以就百度了思路..发现学了也简单,就是多看一位,还是有些畏难,看到多个匹配就不想写...http://blog.csdn.net/pickless/article/details/9043389参考的他的思路,感觉他很强,很多思路都从他这里借鉴的.我添加了注释,就不多解释了.
class Solution {
public:
bool isMatch(const char *s, const char *p) {
if (*p == '\0') return *s == '\0';
if (*(p + 1) == '*') {
while (*s != '\0' && (*s == *p || *p == '.')) { //对于重复的s
if (isMatch(s, p + 2)) {
return true;
}
s++;
}
return isMatch(s, p + 2); //不重复的开始
} else {
if (*s != '\0' && (*s == *p || *p == '.')) //对于一般情况
return isMatch(s + 1, p + 1);
}
return false; //对于s已经匹配完,但p仍有留下的字符.
}
};
7.Simplify Path
题目意思:模拟shell寻找路径,支持单点号和双点号.
用一个栈来模拟就好了.思路就是名字先入栈,然后清理栈.遇见/就判断.其他情况设置一个临时变量来存储其名字.判断的时候,如果名字为”..”表示向上出栈,”.”就不用管了.其余情况入栈.
class Solution {
public:
string simplifyPath(string path) {
stack my_stack;
string str = "";
for(int i = 0; i < path.size(); i++){
if (path[i] == '/'){
if (str == ".."){
if (!my_stack.empty()) my_stack.pop();
} else if (str == "." || str == "") {
;
} else {
my_stack.push(str);
}
str = "";
}
else{
str += path[i];
}
}
if (str == ".."){
if (!my_stack.empty()) my_stack.pop();
} else if (str == "." || str == "") {
;
} else {
my_stack.push(str);
}
if (my_stack.empty())
return "/";
string ans;
while(!my_stack.empty()){
ans = "/" + my_stack.top() + ans;
my_stack.pop();
}
return ans;
}
};
8.Word Search
题目意思: 从二维数组board中搜索一个串,是否匹配word
这一题感觉好复杂...可以任意起点开始搜索...我的第一思路是建立一个哈希,存储每个字符出现的位置,然后再深度搜索..结果写出来好丑...参数很长...pair也用的不熟...百度了一击..发现遍历就可以了.................而且好像也没有用太多时间,因为只要找到就直接递归了...自己写了几种,不知道为什么过不了,就那样了吧。
class Solution {
public:
int ways[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
bool ans = false;
int row, col;
bool exist(vector > &board, string word) {
if(word.size() == 0) return false;
if((row = board.size()) == 0) return false;
if((col = board[0].size()) == 0) return false;
vector> visit(row, vector(col, false));
ans = false;
for (int i = 0; i < row; ++i){
for (int j = 0; j < col; ++j){
visit[i][j] = true;
DFS(i, j, 0, visit, board, word);
if(ans) return true;
visit[i][j] = false;
}
}
return false;
}
void DFS(int i, int j, int cur, vector>& visit, vector> &board, string& word ){
if(ans) return;
if(word[cur] != board[i][j]) return;
if(cur == word.size() - 1){
ans = true;
return;
}
for (int k = 0; k < 4; k++){
int x = i + ways[k][0];
int y = j + ways[k][1];
if(x >= 0 && x < row && y >= 0 && y < col){
if(!visit[x][y]){
visit[x][y] = true;
DFS(x, y, cur + 1, visit, board, word);
visit[x][y] = false;
}
}
}
}
};
9.Evaluate Reverse Polish Notation
题目意思:从tokens中读出逆波兰表达式,然后进行计算.返回结果.
这一题不是太简单了吗...以前做得还是混合的,这直接给出已经拆分好的....正确率还这么低...思路很简单,通过一个栈,数字就入栈,符号就取出两个数,然后把计算结果入栈.最后栈中只有一个数.就是最后的结果.
class Solution {
public:
int evalRPN(vector &tokens) {
int size = tokens.size();
if( size == 0) return 0;
if( size == 1) return atoi(tokens[0]);
stack my_stack;
for(int i = 0 ; i < size ;i++){
int a,b;
if(tokens[i][0] >= '0' && tokens[i][0] <= '9' || tokens[i].size() > 1 && tokens[i][0] == '-'){
my_stack.push(atoi(tokens[i]));
} else {
a = my_stack.top();
my_stack.pop();
b = my_stack.top();
my_stack.pop();
if(tokens[i] == "+"){
my_stack.push(a + b);
} else if(tokens[i] == "-"){
my_stack.push(b - a);
} else if(tokens[i] == "*"){
my_stack.push(b * a);
} else if(tokens[i] == "/"){
my_stack.push(b / a);
}
}
}
int ans = my_stack.top();
my_stack.pop();
return ans;
}
int atoi(string &a){
int size = a.size();
if(size < 1) return 0;
if( size == 1) return a[0] - '0';
bool flag = false;
if( a[0] == '-') flag = true;
int ans = 0;
for(int i = flag ; i < size ; i++){
ans *= 10;
ans += a[i] - '0';
}
if(flag) ans = -ans;
return ans;
}
};
-10.Longest Valid Parentheses
题目意思:返回串中括号连续匹配成功的长度.
这一题我一开始就想着是用差来算最大长度.但是我弄的是left和right记录括号数.写了几句,提交之后发现好多情况不能囊括进来....所以只好用栈来做,直接存储下标.然后根据下标来算即可.但是过不了()()这种情况...后来百度,发现有人和我思路一样,用了点技巧.就是先放进去一个-1,做栈底.那么遇到()()的时候,就可以直接先弹出再与当前的做差.仔细一想对于(()这种情况也是正确的!..感谢大神,赐予我力量....
class Solution {
public:
int longestValidParentheses(string s) {
int size = s.size();
if( size == 0) return 0;
stack my_stack;
my_stack.push(-1);
int ans = 0;
for(int i = 0 ; i < size ;i++){
if(s[i] == '('){
my_stack.push(i);
} else {
if( my_stack.size() > 1){
my_stack.pop();
int cur = my_stack.top();
cur = i - cur;
if(cur > ans) ans = cur;
} else {
my_stack.pop();
my_stack.push(i);
}
}
}
return ans;
}
};
-11.Interleaving String
题目意思: 两个字符串同时交替取出不定长的前缀,拼接成新串.
这一题半天没看懂是什么意思...简单来说就是一部分存s1,再存s2,再存s1...直到最后..又百度了一击...感觉几个大神的博客都差不多.用的动态规划,但说对算法描述都不是特别清楚....对于我这种业余人士..动态规划的解释一边拿听不懂....
简单来说,DP设置一张表,向下表示与s1匹配,向右表示与s2匹配.图坐标中i,j对应的和i+j-1就是对应s3中的位置.如果s3与对应串中的对应位置值相同,那么更新当前dp图中的值.开始只有0,0位置为真,每一行一行的循环.最先更新行首,然后再更新这一行其余部分.虽说很多地方感觉还是有点怪,但还是抄了一遍.再次感谢大神赐予我力量....
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
int size1 = s1.size();
int size2 = s2.size();
if( size1 + size2 != s3.size()) return false;
vector > flag(size1 + 1, vector(size2 + 1, false));
flag[0][0] = true;
for(int i = 0 ; i <= size1 ; i++){
for(int j = 0 ; j <= size2 ; j++){
int cur = i + j - 1;
if(i >= 1 && s1[i - 1] == s3[cur]) flag[i][j] = flag[i - 1][j];
if(j >= 1 && s2[j - 1] == s3[cur]) flag[i][j] = flag[i][j] ||flag[i][j - 1];
}
}
return flag[size1][size2];
}
};
12.Candy
题目意思:给每个小孩发糖,等级高的孩子要比左右两个孩子分的糖多.求最少分多少颗糖.
感觉这个题目...孩子分等级发糖...从小就这么残酷....开始觉得算法直接贪心就好了.先全部初始化为1,从左到右遇到等级高的就把当前值加一,再从右到左,遇到小的就从右边的数+1.发现对于下降来说,很可能从右向左走的时候并不用更新节点值.就这一点要注意.我用三个循环的还比这个2个循环的快28ms...感觉我白优化了....估计是因为<=的判断时间长了吧.至少从局部性更好了.还是贴简洁点的代码好了.
class Solution {
public:
int candy(vector &ratings) {
int size = ratings.size();
if( size == 1) return 1;
vector ans(size, 1);
for(int i = 1 ; i < size ;i++){
if(ratings[i] > ratings[i - 1]){
ans[i] = ans[i - 1] + 1;
}
}
int ret = ans[size - 1];
for(int i = size - 2; i >= 0 ; i--){
if(ratings[i] > ratings[i + 1]){
if(ans[i] <= ans[i + 1]){
ans[i] = ans[i + 1] + 1;
}
}
ret += ans[i];
}
return ret;
}
};
题目意思:从数组中找出非排序的两个数的和为target的下标.
这一题...直接两个循环就过了...当然我优化了一下,本来是在第二层进行加运算,然后与target比较.我直接在第一层for循环里面设置了个变量值为target- numbers[i],在第二层直接比较就行了..这样就减少了好多运算时间.当然时间也花了1476ms......当然,也可以自己创建一个数据结构存值和下标,然后排个序再逼近.做肯定好做,之前也做过类似的.由于现在已经整整刷了两天了!!!(过0点而已..)还是早点休息吧...
class Solution {
public:
vector twoSum(vector &numbers, int target) {
vector ans;
int size = numbers.size();
if(size == 0) return ans;
for(int i = 0 ; i < size ; i++){
int t = target - numbers[i];
for(int j = i + 1 ; j < size ; j++){
if( t == numbers[j]){
ans.push_back(i + 1);
ans.push_back(j + 1);
return ans;
}
}
}
return ans;
}
};
-14.Word Ladder
题目意思:从start串开始,经过dict中的串,转换成end.并且每次转换只能改变单词中的一个字符.返回其变换次数.start==end返回1
一开始感觉这题应该用BFS做..但感觉好像复杂度颇高...只好又一次希望大神赐予我力量了....发现有很多人用图论做...感觉好复杂,我还是找了个类似bfs的方法学着写写算了思路也不太复杂,就是把一个串的每个位置都遍历一遍..然后就得到了这个串的所有可能的下一步,然后再从dict中查找是否有这个单词.如果有的话,那么证明可以继续下一步,将其放入队列.否则就继续找下一个可能...这一题怪就怪在只要有一个不同,返回2...全相同返回1....我简单的优化了一下,发现从1400ms降到了960ms了.其实还可以进一步优化,因为每个查找的单词是等长的.所以可以把每个位置出现的字母记录下来.那么这样就会减少很多次的循环了.不过是否能有时间上的改进,只有做了才知道.毕竟对于常数项循环,编译器可以循环展开,分支跳转和内存读取哪个更快,就得看配置了.
struct Node{
string word;
int num;
};
class Solution {
public:
queue q;
unordered_set del;
int ladderLength(string start, string end, unordered_set &dict) {
if(start==end) return 1;
int size = start.size();
int cnt = 0;
for(int i = 0 ; i < size ; i++)
if( start[i] == end[i]) cnt++;
if( size - cnt == 1) return 2;
queue q;
unordered_set del;
Node a;
a.word = start;
a.num = 0;
q.push(a);
del.insert(start);
int res=-1;
while(!q.empty()){
Node t = q.front();
q.pop();
if(t.word == end){
res = t.num;
break;
}
int size = start.size();
for(int i = 0 ; i < size; i++){
string str = t.word;
for(int j = 0; j < 26; j++){
str[i] = 'a' + j;
if(str != t.word && dict.find(str) != dict.end() && del.find(str) == del.end()){
Node tt;
tt.word = str;
tt.num = t.num + 1;
q.push(tt);
del.insert(str);
}
}
}
}
return res + 1;
}
};
15.Substring with Concatenation of All Words
题目意思: 返回从L中所有串任意顺序组合生成的串在S中的起始下标.
我开始搞了半天,以为是在S中找L里有的字串的地址。。结果发现理解错题目了。。。是L中的所有串任意排序连起来在S中出现的地址。。。不过思路也比较容易想。首先把L中的串都记个数,遇到L中的串就继续找,否则停止查找。自己写了3个小时。。。 开始是内存超了,然后答案不对头,调整好后又超时。。。其实和别人代码也差不多。。就是对于判定多于L长度的串,进行了优化。先贴个讨论里的,我修改了些代码格式。
class Solution {
public:
vector findSubstring(string S, vector &L){
vector ans;
map t_cnt;
map cnt;
int s_size = S.length();
int l_size = L.size();
int w_size = L[0].length();
for(int i = 0; i < l_size ; i++){
cnt[L[i]] ++;
}
string cur;
string temp;
int ccnt = 0;
int start = 0;
for(int j = 0 ; j < w_size ; j++){
ccnt = 0;
start = j;
for(int i = j; i < s_size; i += w_size){
cur = S.substr(i, w_size);
if( cnt.count(cur) == 0 || cnt[cur] == 0 ){
t_cnt.clear();
ccnt = 0;
start = i + w_size;
} else if (t_cnt[cur] < cnt[cur]){
t_cnt[cur]++;
ccnt++;
} else {
temp = S.substr(start, w_size);
while(temp != cur){
t_cnt[temp]--;
ccnt--;
start += w_size;
temp = S.substr(start, w_size);
}
start += w_size;
}
if(ccnt == l_size){
ans.push_back(start);
temp = S.substr(start, w_size);
t_cnt[temp]--;
ccnt--;
start += w_size;
}
}
t_cnt.clear();
}
sort(ans.begin(), ans.end());
return ans;
}
};
下面是超时的。。。
class Solution {
public:
vector findSubstring(string S, vector &L){
vector ans;
map cnt;
int s_size = S.length();
int l_size = L.size();
if(s_size == 0 || l_size == 0) return ans;
int w_size = L[0].length();
if(s_size < l_size * w_size) return ans;
for(int i = 0; i < l_size ; i++) cnt[L[i]] ++;
map t_cnt;
string cur;
string temp;
int ccnt = 0;
int start = 0;
const int first_limit = s_size - w_size * l_size;
for(int i = 0 ; i < first_limit ; i ++){
ccnt = 0;
start = i;
for(int j = i; j < s_size ; j += w_size){
cur = S.substr(j, w_size);
if(cnt[cur] == 0 || t_cnt[cur] > cnt[cur] ){
start = j + w_size;
continue;
}
t_cnt[cur] ++;
ccnt++;
if(ccnt == l_size){
ans.push_back(start);
t_cnt.clear();
ccnt = 0;
start = j + w_size;
}
}
t_cnt.clear();
}
sort(ans.begin(), ans.end());
return ans;
}
};
-16.Palindrome Partitioning II
题目意思:将S串切成回文串,并且返回最小切多少次.
这一题之前的一题直接DFS,我觉得可以用DFS+贪心来做,结果发现算法错误。不能每次取最长的回文来算。有可能有aaabaa的情况,只用一刀。。这后面题怎么都是动态规划。。。好烦。。。思路说起来也不难,但就是自己推不出来。。。思路就是,假设有一个已经回文的串,那么在其首前尾后的字符如果相等,则表示可以继续回文。找了段比较简单的代码来解释下。一开始,假设每个字母都不同,初始化结果数组。然后通过dp,对相应的段表示其值。
class Solution {
public:
int minCut(string s) {
int n = s.size();
vector res(n+1);
for(int i = 0 ; i< n + 1 ; ++i){ //最坏的情况
res[i] = i - 1;
}
vector > p(n, vector(n, false));
for(int i = 0; i< n; ++i){
for(int j = 0; j <= i ; ++j){
if(s[i] == s[j] && (i - j < 2 || p[j + 1][i - 1])){ //区间的两边的一个字符相等,或者其串只有一个或零个字符。
p[j][i] = true;
res[i + 1] = min(res[i + 1], res[j] + 1); //对结果集进行更新,存储可能的最小值。
}
}
}
return res[n];
}
};
-17.Minimum Window Substring
题目意思:从S中找出一段连续字串,并且T中的所有字符在S字串中出现.
这一题思路就是把子串的头和尾设置两个指针,如果头尾指针包含其全部字母,那么头指针增加,否则尾指针增加并加入其需要的元素。看着简单,但想清楚怎么弄还是有些难的。。。写了半天,应是没凑出来。。。而且代码也很丑了。。找了一段代码,感觉其结构还蛮新奇的。思路就是先设置一个统计数组A统计T中各个字符的数,然后再设置一个统计数组B统计两个指针间的有效字符个数。只要有效字符个数和T中个数相同,那么就进入。然后再将其前面多余的非有效元素和超出元素去除。再更新的时候就只更新end,只要end又包含了重复的元素就从start处开始去除。
class Solution {
public:
int cnt[256]={0};
int tmp[256]={0};
string minWindow(string S, string T) {
int sLen=S.size();
int tLen=T.size();
if(tLen==0 || sLen < tLen) return "";
for(int i = 0;i < tLen; i++)
cnt[T[i]]++;
int min_len = 0x7fffffff;
int minBegin = 0;
int begin = 0;
int end = 0;
for(int count = 0; end < sLen; end++){
if(cnt[S[end]] == 0) continue;
tmp[S[end]]++;
if(tmp[S[end]] <= cnt[S[end]])
count++;
if(count == tLen){
while(begin < end){
if(cnt[S[begin]] == 0){
begin++;
continue;
}
if(tmp[S[begin]] > cnt[S[begin]]){
tmp[S[begin]]--;
begin++;
continue;
} else break;
}
int len = end - begin + 1;
if(len < min_len){
minBegin = begin;
min_len = len;
}
}
}
if(min_len == INT_MAX)
return "";
return S.substr(minBegin, min_len);
}
};
-18.Median of Two Sorted Arrays
题目意思: 找出两个排序数组中的中间值.
一开始想到的就是合并之后再二分。。但时间复杂度是O(n+lgn)。。然后感觉完全没必要复制全部的空间,直接算出中间下标,然后设置两个指针指向两个表开头比较两个指针所指向的值就可以了。提交了几次。。发现特殊状况太多了。。。如果m+n是偶数还得把中间两个数一加然后除二。。还有所有数相同,并且长度也相同,那么中位数就是一头一尾的。。。。百度发现可以递归着做。。。代码都差不多。基本的思路就和找第k大的数差不多。先保证a长度小于b,然后比较两个数的第k/2个数,然后每次舍去前k/2个数,再不断递归,直到m==0或者k==1的时候就可以知道是哪个数了。http://blog.csdn.net/yutianzuijin/article/details/11499917 可以参考这个博客。
class Solution{
public:
double findMedianSortedArrays(int A[], int m, int B[], int n){
int total = m + n;
if (total & 0x1)
return findKth(A, m, B, n, total / 2 + 1);
else
return (findKth(A, m, B, n, total / 2)
+ findKth(A, m, B, n, total / 2 + 1)) / 2;
}
double findKth(int a[], int m, int b[], int n, int k){
if (m > n) return findKth(b, n, a, m, k);
if (m == 0) return b[k - 1];
if (k == 1) return min(a[0], b[0]);
int pa = min(k / 2, m);
int pb = k - pa;
if (a[pa - 1] < b[pb - 1])
return findKth(a + pa, m - pa, b, n, k - pa);
else if (a[pa - 1] > b[pb - 1])
return findKth(a, m, b + pb, n - pb, k - pb);
else
return a[pa - 1];
}
};
19.3Sum
题目意思: 三个数的和与目标值相同,返回三个数的组合.
这一题和之前的一道题很像,比较容易做。先排序,然后再用直接一个循环,再设一个循环设置两个变量相互逼近即可。这一题要注意重复元素的去除,否则很容一output超了。。。。我就超了好多次。。。
class Solution {
public:
vector > answer;
vector > threeSum(vector &num) {
int size = num.size();
sort(num.begin(), num.end());
for(int i = 0 ; i < size - 2; i ++){
if(i > 0 && num[i]==num[i-1])
continue;
int j = i + 1;
int k = size - 1;
while(j < k){
if(j > i + 1 && num[j] == num[j - 1]){
j++;
continue;
}
if(k < num.size() - 1 && num[k]==num[k + 1]){
k--;
continue;
}
int sum = num[i] + num[j] + num[k];
if( sum == 0 ){
vector tmp;
tmp.push_back(num[i]);
tmp.push_back(num[j]);
tmp.push_back(num[k]);
answer.push_back(tmp);
j++;
}
else if ( sum < 0 ) j++;
else if( sum > 0) k--;
}
}
return answer;
}
};
-20.Word Break II
题目意思: 返回dict中所有可以组合成S的情况.
这一题就是之前一题的变形,不过之前的只是判断能不能切开。这次是要把全部可以被却开的状态存储起来。感觉应该可以用DFS做。就是在自己电脑上调试没有支持C++11的编译器。。。。devc++居然也不支持。。下了个codeblock也不行。。。这题放弃好了。。。思路大致的就是DFS,加上之前的wordbreak来设置剪枝判断。。参考的是这里的http://blog.csdn.net/a83610312/article/details/12870501代码
class Solution {
public:
vector wordBreak(string s, unordered_set &dict){
int n=s.length();
vector > match(n+1,vector(n+1,false));
for(int i=0;i<=n;i++) match[0][i]=true;
for(int len=1;len<=n;len++){
for(int start=0;start+len<=n;start++){
string tmp=s.substr(start,len);
if(dict.count(tmp)>0)
match[len][start]=true;
else{
for(int left=1;left();
vector ans;
vector had;
dfs(s,0,match,had,ans,dict);
return ans;
}
void dfs(string& s,int k,vector >& match,vector& had,vector& ans,unordered_set &dict)
{
int n=s.length();
if(k>=n){
if(!had.empty()){
string ret;
for(int i=0;i0 && match[n-k-len][k+len]){
had.push_back(tmp);
dfs(s,k+len,match,had,ans,dict);
had.pop_back();
}
}
}
};
21.Divide Two Integers
题目要求: 不准用乘,除取模来做两个整数的除法
这一题我直接用除法就过了。。。这个要求想忽视还是可以的。。。而且貌似也没说道divisor为0的时候的输出是什么。。不过为了锻炼还是写写吧,感觉这一题确实有些细节比较难想全。思路很简单,就是倍增除数,同时设置一个计数累加变量,每次除数倍增,计数累加变量也增加一倍。如果超过被除数,那么减小到最小的被除数。有一个稍微不容易注意到的就是对于-2147483648来说,因为int类型,所以-(-2147483648)仍然是-2147483648。注意这一点就好了。
class Solution {
public:
int divide(int dividend, int divisor) {
unsigned int div_d = dividend;
bool flag = false;
if(dividend < 0){
div_d = -dividend;
flag = true;
}
if(divisor < 0){
divisor = -divisor;
if(flag) flag = false;
else flag = true;
}
if(div_d < divisor) return 0;
unsigned int cur_sor = divisor;
unsigned int cnt = 0;
unsigned int inc = 1;
while(div_d >= cur_sor){
div_d -= cur_sor;
cnt += inc;
inc <<= 1;
cur_sor <<= 1;
while(cur_sor >= divisor && div_d < cur_sor) cur_sor >>= 1, inc >>= 1;
if(cur_sor < divisor) cur_sor = divisor, inc = 1;
}
if(flag) cnt = -cnt;
return cnt;
}
};
-22.Decode Ways
题目要求: A-Z映射为1-26..给出一个数字串,求出所有可能的翻译出来的串数目.
这一题第一感觉可能与斐波那契数列有关。但仔细一想,有些限制,比如两个连续的数只能一个小于2,或者第一个为2下一个小于6.相当于是可以递推。对于0来说是要单独考虑的,其余的还要看前一步。方法就是,申请一个结果集,保存每一步的可能数。然后开始一次一步的往后推。如果当前值为‘0’,如果前一个值不是1或者2,那么无解,返回0;如果是1或者2,那么就直接解集就和f[i-2]一样。如果s[i]不是零,那么就看可否向前组合,能组合则把两条路径的和相加,否则和上一个一样,只分解一个数字。有点像DP。。
class Solution {
public:
int numDecodings(string s) {
int size = s.size();
if(size == 0) return 0;
if(size == 1){
if(s[0] == '0') return 0;
else return 1;
}
if(s[0] == '0') return 0;
int f[size];
memset(f, 0, sizeof(int) * size);
f[0] = 1;
for (int i = 1; i < size; i++) {
if (s[i] == '0') {
if ( s[i - 1] == '0' || s[i - 1] > '2') return 0;
else f[i] = (i > 1 ? f[i - 2] : 1 );
} else {
if ( s[i - 1] == '1' || s[i - 1] == '2' && s[i] <= '6')
f[i] = f[i - 1] + (i > 1 ? f[i - 2] : 1);
else f[i] = f[i - 1];
}
}
return f[size - 1];
}
};
23.String to Integer (atoi)
题目意思:实现一个字符串转整数的函数.
这一题好变态。。原来以前写的都是错的。。。这题算是长见识了。主要在溢出的位置过不去。。。要是汇编,直接判断eflags就好了。。。用java的估计也都爽死了。。有几点要注意。
空指针,空串返回0.正负区间溢出。开头的空格,正负号。
class Solution {
public:
int atoi(const char *str) {
if(str == NULL ) return 0;
if(str[0] == '\0') return 0;
int flag = 1;
int i = 0;
while(str[i] != '\0' && str[i] == ' ') i++;
if(str[i] == '\0') return 0;
if(str[i] == '-') i++, flag = -1;
else if(str[i] == '+') i++;
else if(str[i] < '0' && str[0] > '9') return 0;
long long int ans = 0;
for(; str[i] != 0 ; i++){
if(str[i] >= '0' && str[i] <= '9'){
ans = ans * 10 - '0' + str[i];
if(ans * flag > INT_MAX){
ans = INT_MAX;
return (int)ans;
} else if(ans * flag < INT_MIN){
ans = INT_MIN;
return (int)ans;
}
} else break;
}
return (int)(ans * flag);
}
};
24.Surrounded Regions
题目意思: 一个二维数组中包含X和O.如果O周围的集合被X包围.那么将其转换为X.其余O不变
这一题就是围棋。。。这个第三次就AC了,我果然还是只适合写这种体力题么。。。。。思路也简单,就是设置一个表,表示是否被走过。先扫边框,然后将其坐标加入队列。然后用队列BFS标记表。最后再通过两个循环来设置那些被吃的地方。
struct Node{
int x;
int y;
};
class Solution {
public:
void solve(vector> &board) {
int row = board.size();
if( row == 0) return;
int col = board[0].size();
if( col == 0) return;
vector > my_map(row, vector(col, false));
queue my_qu;
Node t;
for(int i = 0 ;i < row ;i++){
t.x = i;
t.y = 0;
if(board[i][0] == 'O') my_map[i][0] = true, my_qu.push(t);
t.y = col - 1;
if(board[i][col - 1] == 'O') my_map[i][col - 1] = true, my_qu.push(t);
}
for(int j = 1 ; j < col - 1; j++){
t.y = j;
t.x = 0;
if(board[0][j] == 'O') my_map[0][j] = true, my_qu.push(t);
t.x = row - 1;
if(board[row - 1][j] == 'O') my_map[row - 1][j] = true, my_qu.push(t);
}
while( !my_qu.empty()){
Node cur = my_qu.front();
Node input;
my_qu.pop();
if(cur.x > 0 && my_map[cur.x - 1][cur.y] == false && board[cur.x - 1][cur.y] == 'O'){ //up
input.x = cur.x - 1;
input.y = cur.y;
my_qu.push(input);
my_map[input.x][input.y] = true;
}
if(cur.x < row - 1 && my_map[cur.x + 1][cur.y] == false && board[cur.x + 1][cur.y] == 'O'){ //down
input.x = cur.x + 1;
input.y = cur.y;
my_qu.push(input);
my_map[input.x][input.y] = true;
}
if(cur.y > 0 && my_map[cur.x][cur.y - 1] == false && board[cur.x][cur.y - 1] == 'O'){ //left
input.x = cur.x;
input.y = cur.y - 1;
my_qu.push(input);
my_map[input.x][input.y] = true;
}
if(cur.y < col - 1 && my_map[cur.x][cur.y + 1] == false && board[cur.x][cur.y + 1] == 'O'){ //right
input.x = cur.x;
input.y = cur.y + 1;
my_qu.push(input);
my_map[input.x][input.y] = true;
}
}
for(int i = 0 ; i < row ; i++){
for(int j = 0 ; j < col ; j++){
if(my_map[i][j] == false && board[i][j] == 'O')
board[i][j] = 'X';
}
}
}
};
25.Reverse Words in a String
题目意思: 对一个英文句子,将其中的单词的顺序反序.
怎么连着来两道简单题。。。喜出望外啊。。。这题提交是我见过最多的一道了。。。21W+。。。思路也简单就是先反转每个单词,然后再整体反转。但是做了一下,发现坑爹有木有!!!前后有空格要去掉!!还有中间的空格!!!!不过好在算法本身不难,所以AC的比较快。
class Solution {
public:
void reverseWords(string &s) {
int size = s.size();
if(size == 0) return;
string cur;
bool flag = true;
for(int i = 0 ; i < size ; i++){
if(s[i] == ' '){
if(flag){
continue;
}
flag = true;
cur.push_back(s[i]);
} else {
flag = false;
cur.push_back(s[i]);
}
}
if(cur[cur.size() - 1] == ' '){
cur.resize(cur.size() - 1);
}
size = cur.size();
int start = 0;
for(int i = 0 ; i < size ; i++){
if(cur[i] == ' '){
reverse(cur, start, i - 1);
start = i + 1;
} else if( i == size - 1){
reverse(cur, start, i);
}
}
reverse(cur, 0, size - 1);
s = cur;
}
void reverse(string &s,int start , int end){
while(start < end){
swap(s[start], s[end]);
start++;
end--;
}
}
};
-26.Text Justification
题目要求: 给出一些字符串,要求排列在只有L列的二维数组中.并要求空格尽可能的均匀,多的空格靠前填充.并且最后一行一定要从最左开始,并且没有单词可以插入.
这一题真蛋疼。。。算法题目直接给了。。但我解决了好几个坑之后,以为是开头的空格数最大,后面的都一样。。。结果前面的也要平均。。。写的和搜出来的也差不多,但代码已经很丑陋了。。。果断搜网上代码。看了大神的代码,真心觉得模块化太重要了。。。以后有时间再改吧。。。。大神的代码我就解释一下就好了。先和我的思路一样,按长度分段循环,然后长度刚好够就进行连接字符串,加空格的函数也分出来。
class Solution {
public:
vector fullJustify(vector &words, int L) {
vector ret;
int begin = 0, len = 0, n = words.size();
for (int i = 0; i < n; ++i) {
if (len + words[i].size() + (i - begin) > L) {
ret.push_back(connect(words, begin, i - 1, len, L, false));
begin = i;
len = 0;
}
len += words[i].size();
}
ret.push_back(connect(words, begin, n - 1, len, L, true));
return ret;
}
string connect(vector &words, int begin, int end, int len, int L, bool leftJustify) {
string s;
int n = end - begin + 1;
for (int i = 0; i < n; ++i) {
s += words[begin + i];
addSpaces(s, i, n - 1, L - len, leftJustify);
}
if (s.size() < L) s.append(L - s.size(), ' ');
return s;
}
void addSpaces(string &s, int i, int n, int L, bool leftJustify) {
if (n < 1 || i > n - 1) return;
int spaces = leftJustify ? 1 : (L / n + (i < (L % n) ? 1 : 0));
s.append(spaces, ' ');
}
};
27.LRU Cache
题目要求:设计一个算法,要求做到LRU(最近最少使用算法)
这一题让我感觉到了这个OJ的强大。。最近的话可以用链表,每次用过就插到头.快速定位的话就map一下。这还是我第一次用stl中的链表。。算法也比较简单。STL确实还是蛮方便的,早知道就早点学了。。。
struct Node{
int key;
int val;
Node(int k , int v) : key(k) , val(v){}
};
class LRUCache{
int size;
list List;
unordered_map::iterator > my_map;
public:
LRUCache(int capacity) {
size = capacity;
}
int get(int key) {
if(my_map.find(key) != my_map.end()){
list::iterator it = my_map[key];
int value = it->val;
List.erase(it);
List.push_front(Node(key , value));
my_map[key] = List.begin();
return value;
}else return -1;
}
void set(int key, int value) {
if (my_map.find(key) == my_map.end()){
if(List.size() == size){
my_map.erase(List.back().key);
List.pop_back();
}
List.push_front(Node(key , value));
my_map[key] = List.begin();
}else{
list::iterator it = my_map[key];
List.erase(it);
List.push_front(Node(key , value));
my_map[key] = List.begin();
}
}
};
-28.Wildcard Matching
题目意思:实现正则的子集.问号和星号
又是实现正则的子集。。果断翻上面的思路。发现这个题目中aab,“c*a*b”是错误的答案!这不是坑爹吗。。自己写的代码修修补补。。感觉太烂,还是百度了个代码。。大神真的赐予了我很多次力量啊。。。http://www.cnblogs.com/x1957/p/3517096.html思路在这里,我就不多说了。
class Solution {
public:
bool isMatch(const char *s, const char *p) {
const char* star = NULL;
const char* rs = NULL;
while(*s) {
if(*s == *p || *p == '?') { //match
s++; p++;
continue;
}
if(*p == '*') {
star = p; // record star
p++; //match from next p
rs = s; // record the position of s , star match 0
continue;
}
if(star != NULL) { //if have star in front then backtrace
p = star + 1; //reset the position of p
s = rs + 1;
rs = s; //star match 1,2,3,4,5....
continue;
}
return false; //if not match return false
}
while(*p == '*') p++; //skip continue star
return *p == '\0'; // successful match
}
};
递归的算法还是给出来吧,估计面试用得到。
class Solution {
public:
bool isMatch(const char *s, const char *p)
{
if (s == NULL || p == NULL) return false;
if (*p == '\0') return *s == '\0';
if (*p == '*'){
while (*p == '*') ++p;
while (*s != '\0'){
if (isMatch(s, p)) return true;
++s;
}
return isMatch(s, p);
} else if ((*s != '\0' && *p == '?') || *p == *s) {
return isMatch(s + 1, p + 1);
}
return false;
}
};
-29.Word Ladder II
题目意思: 求出所有可以从start到end的路径.路径过程值从dict中取出.
我记得这之前的那道题是枚举每个位置来得到结果的。但这个题我试着用那个思路做了下,发现新串是不太好定位到其矩阵的位置的,而且感觉很慢。
http://blog.csdn.net/doc_sgl/article/details/13341405 我看的他的思路,比较清楚,用得两个数组轮换着倒,倒的时候去除标记。以后有时间再补上这一题的解法。
class Solution {
public:
vector > findLadders(string start, string end, unordered_set &dict)
{
result_.clear();
unordered_map> prevMap;
for(auto iter = dict.begin(); iter != dict.end(); ++iter)
prevMap[*iter] = vector();
vector> candidates(2);
int current = 0;
int previous = 1;
candidates[current].insert(start);
while(true)
{
current = !current;
previous = !previous;
for (auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)
dict.erase(*iter);
candidates[current].clear();
for(auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)
{
for(size_t pos = 0; pos < iter->size(); ++pos)
{
string word = *iter;
for(int i = 'a'; i <= 'z'; ++i)
{
if(word[pos] == i)continue;
word[pos] = i;
if(dict.count(word) > 0)
{
prevMap[word].push_back(*iter);
candidates[current].insert(word);
}
}
}
}
if (candidates[current].size() == 0)
return result_;
if (candidates[current].count(end)) break;
}
vector path;
GeneratePath(prevMap, path, end);
return result_;
}
private:
void GeneratePath(unordered_map> &prevMap, vector& path, const string& word)
{
if (prevMap[word].size() == 0)
{
path.push_back(word);
vector curPath = path;
reverse(curPath.begin(), curPath.end());
result_.push_back(curPath);
path.pop_back();
return;
}
path.push_back(word);
for (auto iter = prevMap[word].begin(); iter != prevMap[word].end(); ++iter)
GeneratePath(prevMap, path, *iter);
path.pop_back();
}
vector> result_;
};
30.Valid Number
题目意思:判断一个字符串是否为一个合法C语言中的常数.
比上面那题更变态。。。各种坑爹的情况。。。我自己是一次次提交试出来的。。。百度了一击,发现有大神还画了DFA。。。。只怪我编译学的都忘光了。。。。。这题应该是我提交最多的一道题了。。。写了一遍觉得太烂,就又写了一遍。。仔细一想,在e左边的数,可以为任意数。e右边的不能为小数。分开写两个循环就好了。。果然分开之后思路清楚多了。
class Solution {
public:
bool isNumber(const char *s) {
if(s == NULL || *s == '\0') return false;
bool dot = false;
bool sci = false;
while(*s != '\0' && *s == ' ') s++;
if(*s == '\0') return false;
bool pre = false;
bool nex = false;
bool flag = false;
while(*s != '\0'){
if(*s == '+' || *s == '-'){
if(pre || flag || dot) return false;
flag = true;
} else if(*s == '.'){
if(dot) return false;
dot = true;
} else if(*s == 'e'){
sci = true;
s++;
break;
} else if(*s >= '0' && *s <= '9'){
pre = true;
s++;
while(*s >= '0' && *s <= '9') s++;
if(*s == '\0') return true;
continue;
} else if(*s == ' '){
while(*s == ' ') s++;
if(*s == '\0' && pre) return true;
else return false;
} else return false;
s++;
}
if( !pre) return false;
flag = false;
dot = true;
while(*s != '\0'){
if(*s == '+' || *s == '-'){
if(nex || flag ) return false;
flag = true;
} else if(*s == '.' || *s == 'e'){
return false;
} else if(*s >= '0' && *s <= '9'){
nex = true;
s++;
while(*s >= '0' && *s <= '9') s++;
if(*s == '\0') return true;
continue;
} else if(*s == ' '){
while(*s == ' ') s++;
if(*s == '\0') break;
else return false;
} else return false;
s++;
}
if(!pre && !nex) return false;
if(sci){
return pre && nex;
} else return pre;
}
};
31.Max Points on a Line
题目意思: 找出数组中两个点连起来的线中包含数组中点最多的线.
这题很奇葩。。。我觉得应该很难,但两个for循环+map记录就过了。。。先给point中的结点按x顺序,这样刚好可以两个循环就够了。但要注意两点间可能有重复和垂直的情况。最后一题,没想到不是很难。。
bool cmp(Point a,Point b){
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
class Solution {
public:
int maxPoints(vector &points) {
int size = points.size();
if(size < 3) return size;
sort(points.begin(), points.end(), cmp);
unordered_map my_map;
int answer = 0;
for(int i = 0; i < size; i++)
{
my_map.clear();
int same = 1;
for(int j = 0; j < size; j++)
{
if(j == i) continue;
if(points[i].x == points[j].x && points[i].y == points[j].y){
same ++;
continue;
}
double k = points[i].x == points[j].x ? INT_MAX : (double)(points[j].y - points[i].y)/(points[j].x - points[i].x);
my_map[k]++;
}
if(my_map.empty()){
if(same > answer) answer = same;
} else {
for(auto i : my_map){
if(i.second + same > answer ) answer = same + i.second;
}
}
}
return answer;
}
};
小结:
1.学会适当的分离出模块.我发现适当的分离出来模块,算法会好写很多啊.特别是先写部分模块,然后再写总体算法的时候.
2.控制畏难情绪.因为本身并不是对算法特别感冒,动态规划和一些感觉有点难的算法都想百度..然后学习到的东西就少了.至今都不能自己独立思考出来转移方程什么的..控制情绪!
3.难以重复.我发现我要想出了某个算法,很难每次写都一样...可能还是知识在脑袋里面停留太少..记不住,还是得多看自己写的程序.
4.先测试的习惯还是没养成...提交的时候总是看运气...
5.学语言还是尽快的学习其内置的数据结构。原来用C的时候都自己写,这次全程用STL明显刷题速度快多了。。。。纯C果然也不适合刷题。。。
6.控制函数代码量.和1差不多,感觉只要多想一下如何控制函数的代码量,很容易就会强迫自己分离出模块了.