http://blog.csdn.net/u012792219/article/details/25838121
轻轻松松走在超神路上
[原]leetcode第一刷_Median of Two Sorted Arrays这道题是我最初刷的那20多道之一,但一直没有过,被各种各样的情况折磨死了,直到把所有其他的题都写完,回来看大神对这道题是怎么处理的时候,才惊叹算法的奇妙。再次验证了我的想法,如果要处理各种各样的特殊情况,一定是算法本身有问题。
之前看过很多有关在两个排序数组中找中位数的解法,大多根据两个数组长度不同分了很多种情况,各种讨论。下面要介绍的方法并没有直接求中位数,而是把求中位数转换成了求两个数组合并之后的第(m+n)/2个数(当然根据总数的奇偶有所不同,不过思路上是没有问题的)。用k来表示进入递归的时候,应该求的两个数列合并之后的那个数,用二分的思想,我们先看A[k/2]与B[k/2]的大小关系,如果A[k/2-1]<B[k/2-1],那么A[0,k/2-1]中有没有可能存在合并之后的第K个数呢?A[k/2-1]大于A中在它之前的k/2-1个数,最好的情况,它也大于B中的在B[k/2-1]之前的k/2-1个数,也就是A[k/2-1]最大在合并后的数列中可以排到第(k/2-1+k/2-1+1)个位置,也即第k-1个位置。因此A[k/2-1]这一部分肯定不包含结果,应该直接扔掉。
对称的,当A[k/2-1]>B[k/2-1]时,有类似的结果。
当A[k/2-1] == B[k/2-1]时呢?说明这个数找到了,返回即可。
在实现的时候有几个技巧,两个数列个数不一样,为了下面编码部分的简便,可以在开头判断一下,如果前面数组个数多,就互换一下。第二点,数组中剩余的的元素可能比k/2要少,这时候就取一个较小的值即可。至于最开始两个数组合并后元素个数奇偶的问题,正是由于我们求的是第k个数而不是中位数,大大简化 了。设总数为t,如果是奇数,就求第t/2+1的那个数,如果是偶数,就分别求第t/2和第t/2+1的数,然后求平均。
28行代码完成了我100多行没有完成的事情,算法太美。
class Solution {
public:
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(m, k/2), 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];
}
}
double findMedianSortedArrays(int A[], int m, int B[], int n) {
int t = m+n;
if(t&0x1){
return findKth(A, m, B, n, t/2+1);
}else{
return (findKth(A, m, B, n, t/2)+findKth(A, m, B, n, t/2+1))/2;
}
}
};
作者:u012792219 发表于2014-5-15 10:34:51 原文链接
阅读:0 评论:0 查看评论
[原]leetcode第一刷_Regular Expression Matching
这道题跟有?和*的那道题很像,不过要简单一些。为什么会简单呢,因为*号只能匹配跟它前面相同的字符。需要注意一点,从aab可以用c*a*b来匹配可以看出,*号可以使他之前的那个字符出现次数变成0。
昨天实验室的同学正好在做这个题,说想用递归做,我想都没想就说用递归肯定超时了。她为什么,我跟人家说因为递归的分支太多了,可怎么也想不起当初自己是怎么写的,回来一看,居然用递归做的,打脸啊。。这个题为什么用递归可以呢?我觉得主要是因为*号的匹配规则,比起可以匹配任意字符来说,只能匹配之前的那个字符显得严格多了,很多分支都被剪掉了。
好,确定了整体的思路是递归,那具体怎么做?想一下递归的分支是怎么走的。同样的,两种符号中,‘.’对我们是不构成威胁的,之所以有很多种情况,是因为*,我们就可以按照*来划分,那是不是按照当前位置是不是*来划分呢?no no no,因为*匹配的是前面的那个字符,因此应该在它前面的那个字符的位置就可以考虑* 的问题,按照当前位置的后一个字符是不是*来划分是个不错的选择。
假设当前位置是i,假设i+1位置不是*,那么i位置必须完全匹配,这里的完全匹配分两种情况,要么是字符相同,要么p[i]='.'。注意'.'必须是跟实际的字符相匹配的,不能匹配'\0'。如果i+1位置是*,分支情况就来了,如果当前位置是不匹配的,那么只能用这个*来讲p[i]的出现次数变成0,也就是继续匹配p+2的位置。如果当前位置是匹配的,那么既可以用p[i]匹配多次(此时之更新s的指针),也可以跳过这个字符(分支通过匹配p+2)。
写成代码会很简介:
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+1) != '*'){
return (*s==*p||(*p=='.'&&*s != '\0'))&&isMatch(s+1, p+1);
}
while(*p==*s || (*p=='.'&&*s!='\0')){
if(isMatch(s, p+2)) return true;
++s;
}
return isMatch(s, p+2);
}
};
作者:u012792219 发表于2014-5-15 9:40:13 原文链接
阅读:31 评论:0 查看评论
[原]leetcode第一刷_Roman to Integer
把罗马的转化成整数就容易多了。
定义一个map,做好所有base跟十进制之间的映射,然后遇到一个base,只要看看这个base是不是比后面的那个base小,如果是的话,说明是4或9的情况,转化后加的应该是两个这两个base对应的十进制之间的差。否则,直接加上base对应的十进制就行了。
class Solution {
public:
int romanToInt(string s) {
if(s == "")
return 0;
int ba = 0, in = 0, res = 0;
map<char, int> baseC;
baseC['M'] = 1000; baseC['D'] = 500; baseC['C'] = 100; baseC['L'] = 50;
baseC['X'] = 10; baseC['V'] = 5; baseC['I'] = 1;
while(in<s.length()){
if(in+1<s.length()&&baseC[s[in]]<baseC[s[in+1]]){
res += baseC[s[in+1]] - baseC[s[in]];
++in;
}else{
res += baseC[s[in]];
}
++in;
}
return res;
}
};
作者:u012792219 发表于2014-5-15 9:13:42 原文链接
阅读:13 评论:0 查看评论
[原]leetcode第一刷_Integer to Roman
这道题当时不会写,是参照discuss写的。
首先要弄明白罗马数字的规则,这个在国外难道是常识吗,为什么题干一点都没讲。。
4000以下一共有下面几种符号:"M", "D", "C", "L", "X", "V", "I",对应到我们十进制为:1000, 500, 100, 50, 10, 5, 1。还有一条非常重要的规则,就是同样的字符最多重复出现三次。这就决定了数位上带4和9的跟普通的数值不一样。数位上4的做法是在比当前数位大5倍的字符左边放一个当前数位表示1的那个字符,例如个位上的四是IV,十位上的四是XL等等。9跟四类似,只不过是在比当前数位大10倍的那个字符左边放一个当前数位表示1的那个字符。
好,下面讲一下怎样把一个数字num转化成罗马数字res。我们从千位开始往前看,如果当前的底比num要大,按理说应该减小底的,但是有个情况,就是下一位如果是个9的且当前位置是以1开头的数字,比如1000,100等,那么res下一个数位应该用base+2 base的形式来表示。如果当前base是以1开头的且num的当前数位是4的时候,res的这个位应该用base base-1。除了是4的这种情况,且当前数位比base要大,那么就在res后面加上base。如果这些情况都不满足,也即base并不是以1开头的或者后面接的不是9,都是直接减少base。要注意,每当更新了res之后,要把用掉的那部分数从num中减掉。
说了这么多,其实还是代码直观一些:
string bit[7] = {"M", "D", "C", "L", "X", "V", "I"};
int mref[7] = {1000, 500, 100, 50, 10, 5, 1};
class Solution {
public:
string intToRoman(int num) {
string res = "";
int base = 0;
while(num){
if(base%2==0 && num/mref[base]==4){
res += bit[base];
res += bit[base-1];
num -= 4*mref[base];
}else if(num>=mref[base]){
res += bit[base];
num -= mref[base];
}else if(base%2==0 && num/mref[base+2]==9){
res += bit[base+2];
res += bit[base];
num -= mref[base+2]*9;
}else{
base++;
}
}
return res;
}
};
作者:u012792219 发表于2014-5-14 22:36:55 原文链接
阅读:134 评论:0 查看评论
[原]leetcode第一刷_Longest Common Prefix
这个题貌似比想象中的要简单很多。完全用不上前缀树什么的高级的数据结构。话说室友经常跟我说前缀树的事情,但是我还没学会,等更新完这个系列,专心钻研一下。
因为问的是最长公共前缀,只要一个一个的比对下去就行。。全一样,就加入到结果中,否则直接退出,边界什么的考虑一下。
class Solution {
public:
string longestCommonPrefix(vector<string> &strs) {
string res = "";
if(strs.size()<=0)
return res;
if(strs.size() == 1)
return strs[0];
int pos = 0;
while(true){
int i=0;
for(;i<strs.size()-1&&strs[i]!=""&&pos<strs[i].length()&&strs[i][pos] == strs[i+1][pos];i++);
if(i<strs.size()-1)
break;
else{
pos++;
res += strs[0][pos-1];
}
}
return res;
}
};
作者:u012792219 发表于2014-5-14 21:52:29 原文链接
阅读:26 评论:0 查看评论
[原]leetcode第一刷_4Sum
用暴力水过了两个,正在沾沾自喜,当看到连续加的规模增加到4时,眼泪直接掉下来了,新想这下我这水货原形毕露了,没想到。。
我抱着试试看的心态,又写了一种水水的暴力解,每次固定前面的两个节点,后面的两个按照twosum那套来搞,搞完了移动之前固定的右边的那个点,最后再移动固定的左边的那个点,居然也过了。。
刚想起来其实twosum有一种很优雅的解法,建一个hash表,从头扫描数组,遇到一个数,查找这个数与target的差在不在hash表里,如果在,说明这两个数找到了,他们的下标分别是找到的那个数在hash表中的索引,以及当前这个数的索引。如果不在hash表里,那就把这个差放入hash表。比头尾双指针看上去高端一些。
class Solution {
public:
vector<vector<int> > fourSum(vector<int> &num, int target) {
vector<vector<int> > res;
if(num.size()<4)
return res;
sort(num.begin(), num.end());
for(int i=0;i<num.size()-3;i++){
if(i&&num[i] == num[i-1]) continue;
int j = i+1;
int k = num.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 l = j+1;
int m = k;
while(l<m){
if(l>j+1&&num[l]==num[l-1]) {l++; continue;}
if(m<k&&num[m]==num[m+1]) {m--; continue;}
int sum = num[i]+num[j]+num[l]+num[m];
if(sum < target) l++;
else if(sum>target) m--;
else{
vector<int> tpres;
tpres.push_back(num[i]);
tpres.push_back(num[j]);
tpres.push_back(num[l]);
tpres.push_back(num[m]);
//cout<<num[i]<<" "<<num[j]<<" "<<num[l]<<" "<<num[m]<<endl;
res.push_back(tpres);
l++;
}
}
j++;
}
}
return res;
}
};
作者:u012792219 发表于2014-5-14 21:48:12 原文链接
阅读:92 评论:0 查看评论
[原]leetcode第一刷_3Sum Closest
其实这道题跟上一道完全一样啊,只不过每次sum都有意义了。每次算出一个sum,看看跟target之间的差距是不是缩小了,缩小了就更新一下解。
class Solution {
public:
int threeSumClosest(vector<int> &num, int target) {
int res = 0x7fffffff, min_delta = 0x7fffffff;
if(num.size()<=2)
return res;
sort(num.begin(), num.end());
for(int i=0;i<num.size()-2;i++){
int j = i+1;
int k = num.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(abs(sum-target)<min_delta){
min_delta = abs(sum - target);
res = sum;
if(min_delta == 0)
return res;
}
if(sum>target) k--;
else if(sum<target) j++;
}
}
return res;
}
};
作者:u012792219 发表于2014-5-14 21:32:03 原文链接
阅读:18 评论:0 查看评论
[原]leetcode第一刷_3Sum
估计大家都会做twoSum,一头一尾两个指针,跟据和的大小移动就行了。
3sum能不能用相同的方法呢,我尝试用暴力做,居然过了。思路是先把数组排个序,让相同数字的都靠在一起,然后固定一个数,其他两个数就按照twosum的那一套来,只不过计算sum的时候多算了一个数而已。需要注意一个问题,靠在一起一样的数,只能在第一次遇到它的时候用,更准确一点说,每个相同的数,只有一次作为i或j或k的机会,而且不用担心是不是会漏掉情况,如果数组是111,要求3,这不都用了吗,是的,但是是i,j,k各用了他们一次。表达的不是很好,看代码应该就明白了。这个解法太暴力,不优雅。
class Solution {
public:
vector<vector<int> > threeSum(vector<int> &num) {
vector<vector<int> > res;
if(num.size() <= 2)
return res;
sort(num.begin(), num.end());
for(int i=0;i<num.size()-2;i++){
if(i&&num[i-1] == num[i]) continue;
int j = i+1;
int k = num.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) k--;
else if(sum<0) j++;
else{
vector<int> tp_res;
tp_res.push_back(num[i]);
tp_res.push_back(num[j]);
tp_res.push_back(num[k]);
res.push_back(tp_res);
j++;
}
}
}
return res;
}
};
作者:u012792219 发表于2014-5-14 21:28:25 原文链接
阅读:103 评论:0 查看评论
[原]leetcode第一刷_Letter Combinations of a Phone Number
水题。数字一共就9个,去掉1是用来显示标点的,剩下8个。
穷举一下map,然后有几个数字,就输出这几个数字的排列,是一个dfs嘛。
map<char, string> num;
void allCombinations(string &digits, int start, int len, string &pres, vector<string> &res){
if(len == digits.length()){
res.push_back(pres);
//cout<<pres<<endl;
return;
}
for(int i=0;i<num[digits[start]].length();i++){
pres[len] = num[digits[start]][i];
allCombinations(digits, start+1, len+1, pres, res);
}
}
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> res;
if(digits.length()<=0){
res.push_back("");
return res;
}
num['2'] = "abc";
num['3'] = "def";
num['4'] = "ghi";
num['5'] = "jkl";
num['6'] = "mno";
num['7'] = "pqrs";
num['8'] = "tuv";
num['9'] = "wxyz";
string pres(digits.length(),'#');
allCombinations(digits, 0, 0, pres, res);
return res;
}
};
作者:u012792219 发表于2014-5-14 21:12:05 原文链接
阅读:91 评论:0 查看评论
[原]leetcode 第一刷_Remove Nth Node From End of List
链表问题里经典的双指针,一个先动,一个后动,然后同时动,无他,但手熟尔。
class Solution {
public:
ListNode *removeNthFromEnd(ListNode *head, int n) {
if(!head)
return head;
ListNode *pre = head, *end = head;
int tp = 0;
while(tp<n&&end){
tp++;
end = end->next;
}
if(tp<n)
return head;
if(tp==n&&end==NULL){
head = pre->next;
delete pre;
return head;
}else{
while(end->next){
pre = pre->next;
end = end->next;
}
ListNode *del = pre->next;
pre->next = del->next;
delete del;
return head;
}
}
};
作者:u012792219 发表于2014-5-14 20:20:55 原文链接
阅读:31 评论:0 查看评论
[原]leetcode第一刷_Valid Parentheses
挺有意思的。
给定的不是一种括号,而是三种括号,虽然没有考虑三种之间的优先级,不过也不能用之前的方法一下搞出来。从头到尾扫描字符串,遇到左括号,无论是哪一种,都直接压入栈中,当遇到一个是一个右括号,要看看栈顶是不是跟他匹配的那个左括号,如果是的话,就出栈,要么,就说明出错了。至于是不是匹配的括号,我是用一个map来看的。
我当时写的时候思路不如这个清晰,写的还有点乱。
class Solution {
public:
bool isValid(string s) {
map<char, char> cha;
stack<char> ms;
cha['('] = ')'; cha[')'] = '#';
cha['{'] = '}'; cha['}'] = '#';
cha['['] = ']'; cha[']'] = '#';
if(s.length()<2)
return false;
ms.push(s[0]);
for(int i=1;i<s.length();i++){
if(!ms.empty()&&cha[ms.top()] == s[i])
ms.pop();
else if(cha[s[i]] == '#')
return false;
else
ms.push(s[i]);
}
if(ms.empty())
return true;
else
return false;
}
};
作者:u012792219 发表于2014-5-14 20:12:10 原文链接
阅读:21 评论:0 查看评论
[原]leetcode第一刷_Generate Parentheses
这道题还挺好的,有一点意思。
一共有两个候选的元素,每个候选元素最多可以用n次,他们两个之间还有个限制条件是要加入右括号必须要求未匹配的左括号数大于0才行。通过这些应该已经有思路了,递归的时候,每次都可以由两种选择,要么在下个位置上放左括号,要么放右括号,需要记录两个量,一个是一共放了多少个左括号了,另一个是当前未匹配的左括号有多少个。这样,当未匹配的左括号数大于0时,才可以继续放右括号,也就是右括号的数量一定不会大于左括号的数量,不需要保存它的个数。
这种题目,代码总是比语言更好理解。
void generateAll(int left, int hleft, int n, int pos, string &pres, vector<string> &res){
if(pos == 2*n){
res.push_back(pres);
//cout<<pres<<endl;
return;
}
for(int i=0;i<2;i++){
if(i==0&&hleft<n){
pres[pos] = '(';
generateAll(left+1, hleft+1, n, pos+1, pres, res);
}else if(i==1&&left>0){
pres[pos] = ')';
generateAll(left-1, hleft, n, pos+1, pres, res);
}
}
}
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
if(n<=0)
return res;
string pres(2*n, '#');
generateAll(0, 0, n, 0, pres, res);
return res;
}
};
作者:u012792219 发表于2014-5-14 19:50:05 原文链接
阅读:114 评论:0 查看评论
[原]leetcode第一刷_Merge k Sorted Lists
看到这道题,我吐了。做过两个merge的,就已经够恶心了,居然k个都要merge。。
写的时候我没考虑太多,从头到尾的merge,merge的结果指针放到后一个的位置上,这样输出最后一个指针就可以了。更好的方式应该是用类似归并的方法。一层一层的往上合并,回头把这个的代码写一下,发上来。
ListNode* merge(ListNode* a, ListNode *b){
if(a == NULL)
return b;
if(b == NULL){
return a;
}
if(a->val>b->val){
ListNode* tp = a;
a = b;
b = tp;
}
ListNode* head = a, *pre = a;
a = a->next;
head->next = NULL;
while(a && b){
if(a->val<=b->val){
pre->next = a;
pre = a;
a = a->next;
pre->next = NULL;
}else{
pre->next = b;
pre = b;
b = b->next;
pre->next = NULL;
}
}
if(a){
pre->next = a;
}
if(b){
pre->next = b;
}
return head;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
if(lists.size() == 0)
return NULL;
if(lists.size() == 1)
return lists[0];
int len = lists.size();
for(int i=1;i<len;i++){
lists[i] = merge(lists[i-1], lists[i]);
}
return lists[len-1];
}
};
作者:u012792219 发表于2014-5-14 19:35:25 原文链接
阅读:19 评论:0 查看评论
[原]leetcode第一刷_Swap Nodes in Pairs
这道题实际上是上一道的一个特例嘛,每次都只交换相邻的两个,写起来更加简单了。
class Solution {
public:
ListNode *swapPairs(ListNode *head) {
if(!head||!head->next) return head;
ListNode *p1=head, *p2=head->next, *p3=p2 ->next, *p0=NULL;
ListNode *newHead = p2;
while(p2){
if(p0) p0->next = p2;
p2->next = p1;
p1->next = p3;
p0 = p1;
p1 = p3;
if(p1) p2 = p1->next;
else p2 = NULL;
if(p2) p3 = p2->next;
else p3 = NULL;
}
return newHead;
}
};
作者:u012792219 发表于2014-5-14 19:27:19 原文链接
阅读:22 评论:0 查看评论
[原]leetcode第一刷_Reverse Nodes in k-Group
之前说过很多遍,链表的反向是多么的重要。这道题又用到了。
链表的题是多么的无趣,也不知道面试的时候是不是会问这种。第一次反向的时候,要记得把头指针改一下,还有就是如果剩下的个数不够了,就不要翻转了,返回就行。
class Solution {
public:
void reversePart(ListNode* start){
if(start == NULL || !start->next) return;
ListNode *p=start, *pp = start->next, *ppp;
p->next = NULL;
while(pp != NULL){
ppp = pp->next;
pp->next = p;
p = pp;
pp = ppp;
if(pp) ppp = pp->next;
}
}
ListNode *reverseKGroup(ListNode *head, int k) {
if(!head||!head->next) return head;
ListNode *right, *pre=NULL, *newHead = head, *newStart = head, *ne;
int i;
while(newStart){
i=1;
right = newStart;
while(i<k&&right){
right = right->next;
i++;
}
if(!right) return newHead;
if(!pre) newHead = right;
ne = right->next;
right->next = NULL;
reversePart(newStart);
if(pre)
pre->next = right;
pre = newStart;
newStart->next = ne;
newStart = ne;
}
return newHead;
}
};
作者:u012792219 发表于2014-5-14 19:25:14 原文链接
阅读:20 评论:0 查看评论
[原]leetcode第一刷_Remove Element
水题。
这道题的题干直接把思路说出来了啊。。惊呆了。
两个指针,一前一后,遇到要去掉的数字就把后面的换上来,呵呵。
inline void swap(int &a, int &b){
int tp = a;
a = b;
b = tp;
}
class Solution {
public:
int removeElement(int A[], int n, int elem) {
if(A == 0)
return 0;
int end = n-1;
while(A[end--] == elem);
for(int i=0;i<end;i++){
if(A[i] == elem){
swap(A[i], A[end]);
while(A[--end] == elem);
}
}
return end+1;
}
};
作者:u012792219 发表于2014-5-14 19:19:40 原文链接
阅读:23 评论:0 查看评论
[原]leetcode第一刷_Implement strStr()
判断字串,直接kmp。
int p[1000000];
void getP(char *needle){
p[0] = -1;
int j=-1;
for(int i=1;needle[i]!='\0';i++){
while(j>=0&&needle[j+1]!=needle[i]) j = p[j];
if(needle[j+1] == needle[i])
j++;
p[i] = j;
}
}
class Solution {
public:
char *strStr(char *haystack, char *needle) {
if(haystack == NULL || needle == NULL)
return NULL;
if(!*needle) return haystack;
int j=-1, len=0;
while(needle[len] !='\0'){
len++;
}
getP(needle);
for(int i=0;haystack[i] != '\0';i++){
while(j>=0&&haystack[i] != needle[j+1]) j = p[j];
if(haystack[i] == needle[j+1])
j++;
if(needle[j+1] == '\0'){
return haystack+i-len+1;
}
}
return NULL;
}
};
作者:u012792219 发表于2014-5-14 19:15:51 原文链接
阅读:23 评论:0 查看评论
[原]leetcode第一刷_Divide Two Integers
在剑指offer上有一道不用加减的加法,给了很多稀奇古怪的解法,今天想找来看看,找不到这本书了。。
我不知道左移和右移算不算作弊了,因为本质上还是乘除了。不能用乘除,可以用加减。一个一个的加上去,看看能加多少个除数,果断超时了。计算机很神奇,最神奇的地方就是能把多复杂的事情,都转化成01两种操作,二分,二倍,像算法里的01,是很多算法的基础。当一个个加不行的时候,想想每次double嘛,不仅double除数,还要double计数值。double了有个问题,如果double之后会超过被除数怎么办呢?那这次就别double了,先减去一下目前已经统计过的除数,下次除数从头开始加。
class Solution {
public:
int divide(int dividend, int divisor) {
int res = 0, count;
long long sum;
bool flag = false;
if((dividend<0&&divisor>0)||(dividend>0&&divisor<0))
flag = true;
long long tdividend = dividend;
long long tdivisor = divisor;
long long mdividend = abs(tdividend);
long long mdivisor = abs(tdivisor);
if(mdivisor>mdividend) return 0;
while((mdivisor>1)&&((mdivisor&1) == 0)){
mdividend>>=1;
mdivisor>>=1;
}
if(mdivisor==1) return flag?-mdividend:mdividend;
while(mdividend>=mdivisor){
count = 1;
sum = mdivisor;
while(mdividend>=sum+sum){
sum += sum;
count += count;
}
res += count;
mdividend -= sum;
}
return flag?-res:res;
}
};
作者:u012792219 发表于2014-5-14 19:13:14 原文链接
阅读:107 评论:0 查看评论
[原]leetcode第一刷_Substring with Concatenation of All Words
这道题我并没有想到太好的解法,用了两个map,时间效率很差,勉强过了。
要求一个字串,是字典里所有单词的一个种组合。显然不能先求出所有组合,再看是不是字串,这时间复杂度太高。还好题目中说明了所有单词的长度是一样的,这大大简化了操作。我的做法是建立一个map,这个map以单词为键,以出现次数为值,哈哈,没想到吧~这里很容易忽视的一个问题是同一个单词可能在字典里存了多次。然后开始扫字符串,每次取单词长度,看看在不在字典里,在的话就把可用个数减1,这也就是为什么要用两个map,一个用来保存原始情况,一个用来中间处理。如果出现可用个数小于0的情况,说明这一段是不行的,应该pass掉。
这里我有一个很纠结的问题,就是下次开始的位置,这个位置应该每次只移动1,还是像kmp那样每次找一个前面匹配的位置回退一下。回头应该验证一下。
class Solution {
public:
vector<int> findSubstring(string S, vector<string> &L) {
vector<int> res;
int msize = L.size();
if(msize == 0)
return res;
int len = L[0].length();
int olen = msize*len;
int slen = S.length();
map<string, int> visited, vis;
for(int i=0;i<msize;i++){
visited[L[i]]++;
}
int scope = slen-olen;
string tp;
for(int i=0;i<=scope;i++){
int j=0;
vis = visited;
for(;j<olen;j+=len){
tp = S.substr(i+j, len);
if(vis[tp]<=0)
break;
vis[tp]--;
}
if(j == olen){
res.push_back(i);
}
vis.clear();
}
}
};
作者:u012792219 发表于2014-5-14 18:49:23 原文链接
阅读:102 评论:0 查看评论
[原]leetcode第一刷_Next Permutation
这个题想了好久没想出来,后来查了一下,原来是STL里的一个函数,直接给前辈跪了。
要想在O(N)的时间内求得,必须找到当前排列与后一个排列之间的关系。举几个例子,13245的下一个排列是13254,15432的下一个排列是21345,有没有什么联系呢?我觉得不说出来的话是很难发现的。后一个排列一定是由前一个排列置换得到的,最简单的情况,12345,下一个排列是12354,置换的一定是原来是顺序的那两个数字,置换之后,他们变成逆序的。顺序对可能有很多,应该置换哪一对儿呢?答案是最相近的那一对,这个相近指的不是他们在位置上的相近,而是数值上的相近,道理很简单,下一个排列在置换之后要增加的最小。那怎样找一对顺序又相近的数字呢?方法是从后往前找,不断比较两个相邻元素是不是顺序的,不是顺序的就继续往前(说明这一小段是从后往前递增的,也就是逆序的),知道找到顺序对或者到达最开始,到达最开始都不满足的话,说明整个序列是逆序的,按照要求,应该把所有元素都逆序一下。如果找到这样一个顺序对,那么应该置换的不是这两个元素,而应该是从尾部找的,第一个比顺序对中的前面的那个元素(较小的那个)大的元素,来跟这个元素做置换。举个例子,15432的第一个顺序对是15,较小的数是1,不应该置换15,因为5可能大太多了,而是从尾部找第一个比1大的,就是2,置换之后,得到的是25431。结束了吗?当然没有,刚刚置换过的两个元素之间的这部分一定是递增的,当一个较大的被换到前面之后,它后面的应该恢复顺序排列才行,所以要把之间的这部分,包括被换到后面的那个数,逆序排列。
我觉得这个还是比较难理解的,从头开始找规律也很困难,只能自己找例子多跑几遍。看了源码的感觉就是”不能赞一词“,严谨精确。
class Solution {
public:
void nextPermutation(vector<int> &num) {
vector<int>::iterator first = num.begin(), last = num.end(), i, ii, j;
if(first == last) return;
i=first;
++i;
if(i == last) return;
i = last;
--i;
while(true){
ii = i;
--i;
if(*i<*ii){
j = last;
while(*(--j)<=*i);
iter_swap(i, j);
reverse(ii, last);
return;
}
if(i == first){
reverse(first, last);
return;
}
}
}
};
作者:u012792219 发表于2014-5-14 18:29:38 原文链接
阅读:86 评论:0 查看评论