存储IP地址
- 给出一个字符串,仅包含数字位,返回可能的IP地址组合。比如给出字符串"25525511135",返回["255.255.11.135", "255.255.111.35"],顺序是无关的。
- 思路:关于IP地址的规则:如果字符串的长度大于12或者小于4,都不是合法的IP地址,IP地址共有4个字段,每一个字段都是0~255之间的整数,但是在字符串中,不可以出现诸如"255.025.100.0"这种地址,因为第二字段"025"的"0"会被省去,但是最后一位"0"却是允许的。仔细地考虑这些规则,建立一个函数,判断一个字符串是否是合法的num位IP地址,然后:s[1...n]间所有合法的4位IP地址就是:s[1]是合法的,加上s[2...n]构成的所有合法的3位IP地址……以此类推。
- 实现:
-
class Solution { public: vector<string> restoreIpAddresses(string s) { return ipAddresses(s, 0, 4); } vector<string> ipAddresses(const string& s, int start, int num){ int n = s.length() - start; vector<string> rslt; if(n > num*3 || n < num){ return rslt; } if(num == 1){ string ts = s.substr(start); if(validNum(ts)){ rslt.push_back(ts); } } else{ // num=2,3,4 int m = 3>n ? n : 3; for(int i=1; i<=m; i++){ string ts = s.substr(start, i); vector<string> fss = ipAddresses(s, start+i, num-1); if(validNum(ts)){ for(int j=0; j<fss.size(); j++){ rslt.push_back(ts+"."+fss[j]); } } } return rslt; } } bool validNum(const string& s){ if(s.length() == 0){ return false; } if(s[0]=='0' && s.length()!=1){ return false; } stringstream ss; ss << s; int fld; ss >> fld; if(fld <= 255 && fld >= 0){ return true; } else{ return false; } } };
解码
- 某种消息包含了A-Z的字符,该消息使用了以下密码表加密:
'A' -> 1 'B' -> 2 ... 'Z' -> 26
给出一则加密后的消息,试图给出所有可能的明文的种树,比如给出字符串"12",返回2,因为该字符串可被解码为两种明文:"AB"或"L"。11
- 思路:仍然是动态规划,而且比较简单。建立一个数组nd,nd[i]表示字符串的前i+1位s[0...i]可被解码的种类数,nd[p]的取值可以根据nd[p-1]和nd[p-2],以及相关的细小规则给出。
- 实现:
-
class Solution { public: int numDecodings(string s) { int n = s.length(); if(n==0){ return 0; } vector<int> nd(n+1, 0); nd[0]=1; if(n>=1){ nd[1]= s[0]=='0' ? 0 : 1; } for(int i=1; i<n; i++){ // To do : set nd[i+1] if(s[i]=='0'){ if(s[i-1]=='1' || s[i-1]=='2'){ nd[i+1] = nd[i-1]; } else{ nd[i+1] = 0; } } else{ // s[i] != 0 if(s[i-1]=='0'){ nd[i+1] = nd[i]; } else if(s[i-1]=='1'){ nd[i+1] = nd[i] + nd[i-1]; } else if(s[i-1]=='2'){ if(s[i]>'6'){ nd[i+1] = nd[i]; } else if(s[i]<='6'){ nd[i+1] = nd[i] + nd[i-1]; } } else{ nd[i+1] = nd[i]; } } } return nd[n]; } };
灰色编码排列
- 灰色编码排列是一种二进制的编码方式,两个连续的元素只有一个bit有差异。给出一个整数n,表示数据元素的位数,然后返回整个灰色编码排列。比如,给出数字2,返回序列[0,1,3,2],因为灰色编码如下:
00 - 0 01 - 1 11 - 3 10 - 2
- 思路:还是很简单的,生成n位的灰色编码排列,先生成n-1位的排列($2^n$个元素),然后,先用0接在n-1位的排列每个元素的最前面,然后再用1做相同的事情,最后将两个排列的元素接在一起。需要注意的是:为了使变化只有1位,所以用0接在n-1位排列和用1接n-1位排列时,n-1位的排列的顺序应当是相反的,这样,用0接n-1位排列的结果的最后一个元素,和用1接的最后一个元素,它们才相差1位,那就是第一位(0和1)。至于是先用0接还是先用1接,其实是无所谓的。
- 实现:
class Solution { public: vector<int> grayCode(int n) { int x1=0; int x2=1; vector<int> gc; if(n==0){ gc.push_back(0); } if(n==1){ gc.push_back(x1); gc.push_back(x2); } if(n>1){ vector<int> tc = grayCode(n-1); for(int i=0; i<tc.size(); i++){ gc.push_back(pow(2,n-1)*x1+tc[i]); } for(int i=tc.size()-1; i>=0; i--){ gc.push_back(pow(2,n-1)*x2+tc[i]); } } return gc; } };
两个已排序数组的中位数
- 在代价O(log(m+n))内找到两个已排序数组元素的中位数。
- 思路:假设两个已排序数组分别为 $A\{a_{1},a_{2}...a_{n}\}$ 和 $B\{b_{1},b_{2}...b_{m}\}$。取 $A$ 和 $B$ 的中位数 $a_{mid-A}$ 和 $b_{mid-B}$ 并比较大小:如果 $a_{mid-A}$ 比 $b_{mid-B}$ 小,那么数组 $A$ 的前半部分不可能包含中位数了,数组 $B$ 的后半部分也不可能包含中位数,因此砍掉 $A$ 的前 $x$ 个元素和 $B$ 的后 $x$ 个元素($x$ 为 数组 $A$ 中的前半部分的元素个数 和 数组 $B$ 中的后半部分的元素个数 中的较小者)。
- 实际用代码实现的时候,考虑了数组 $A$ 和 $B$ 的奇偶性,如果某个数组有偶数个元素,就选出两个中位数,并作一些易于理解但更细致的处理,比如:当具有偶数个元素的数组 $A$ 的较小中位数都比具有奇数个元素的数组 $B$ 的中位数都大的时候,才符合 $A$ 的中位数大于 $B$ 的中位数这样的条件。
- 实现:
int findMedianSortedArrays(int A[], int m, int B[], int n) { int lenA = m/2; int lenB = n/2; int len = lenA<lenB?lenA:lenB; bool frontcutA; if (m%2==1 && n%2==1){ int midA = m/2; int midB = n/2; if (A[midA]<B[midB]){frontcutA = true;} if (A[midA]>B[midB]){frontcutA = false;} if (A[midA]==B[midB]){return A[midA];} } if (m%2==1 && n%2==0){ int midA = m/2; int midB1 = n/2-1;int midB2 = n/2; if (A[midA]<B[midB1]){frontcutA = true;} if (A[midA]>B[midB2]){frontcutA = false;} if (A[midA]>=B[midB1] && A[midA]<=B[midB2]){return A[midA];} } if (m%2==0 && n%2==1){ int midA1 = m/2-1;int midA2 = m/2; int midB = n/2; if (A[midA2]<B[midB]){frontcutA = true;} if (A[midA1]>B[midB]){frontcutA = false;} if (A[midA1]<=B[midB] && A[midA2]>=B[midB]){return B[midB];} } if (m%2==0 && n%2==0){ int midA1 = m/2-1;int midA2 = m/2; int midB1 = n/2-1;int midB2 = n/2; if (A[midA2]<B[midB1]){frontcutA = true;} if (A[midA1]>B[midB2]){frontcutA = false;} else{return A[midA1]>B[midB1]?A[midA1]:B[midB1];} } if (frontcutA){ return findMedianSortedArrays(A+len, m-len, B, n-len); } else{ return findMedianSortedArrays(A, m-len, B+len, n-len); } }
扭曲的字符串
- 给定一个字符串,这个字符串可以被分割成一棵二叉树,树的每个叶子节点都是单个字母,比如字符串"great"可以被分为:
great / \ gr eat / \ / \ g r e at / \ a t
对任意的非叶子节点,交换左子树和右子树,能够得到一个新的字符串。该操作允许进行任意次,比如这样一个字符串"rgtae":
rgtae / \ rg tae / \ / \ r g ta e / \ t a
就可以先通过交换eat节点(的左右子树),然后交换at节点,最后交换gr节点来生成。
现在给定字符串s1和s2,判断是否能够通过这种操作来将s1转换为s2。 -
思路:
-
首先,如果两个字符串的长度不一样,或者长度一样但是包含的字母不一样(这一点通过排序之后比较实现),那么是没可能转换成功的,这是显见的。
-
其次,如果两个字符串包含相同的字母,也不一定能够成功,比如"abcd"和"bdac"(不信你可以试试)。
-
在这种情况下,可以转换成功地条件是:存在这么一个数p<n(字符串的长度),使得(s1的前p位能够转换为s2的前p位,而且s1的后n-p位能够转换为s2的后n-p位)或者(s1的前p位能够转换为s2的后p位,而且s1的后n-p位能够转换为s2的前n-p位):如果存在这么一个p,那么就成功了。
-
- 实现:
-
class Solution { public: bool isScramble(string s1, string s2) { if (s1.length()!=s2.length()){ return false; } int n = s1.length(); return isScramble(s1, s2, 0, 0, n); } bool isScramble(const string& s1, const string& s2, int n, int m, int p){ if (!hasSameLetters(s1, s2, n, m, p)){ return false; } if(p==2 || p==1){ return true; } for (int i=0; i<p-1; i++){ if(isScramble(s1, s2, n, m, i+1) && isScramble(s1, s2, n+i+1, m+i+1, p-i-1)){ return true; } if(isScramble(s1, s2, n, m+p-i-1, i+1) && isScramble(s1, s2, n+i+1, m, p-i-1)){ return true; } } return false; } bool hasSameLetters(const string& s1, const string& s2, int n, int m, int p){ vector<char> v1, v2; for (int i=0; i<p; i++){ v1.push_back(s1[n+i]); v2.push_back(s2[m+i]); } sort(v1.begin(), v1.end()); sort(v2.begin(), v2.end()); return v1==v2; } };
分割链表
- 给出一个单链表,和一个值x,将单链表分为前后两部分,前一部分的所有元素都小于x,后一部分的所有元素都大于等于x,而且前后部分元素的相对位置和原链表一致。比如给出链表1->4->3->2->5->2和值3,返回链表1->2->2->4->3->5。
- 思路:既然没有要求原地排序,而且还需要元素相对位置和原链表一致,那这道题实在太简单了,直接遍历一遍链表,按照条件复制到两个链表中,再将两个链表接起来就是。
- 实现:
class Solution { public: ListNode *partition(ListNode *head, int x) { ListNode* h1 = new ListNode(0); ListNode* h2 = new ListNode(0); ListNode* t1 = h1, *t2 = h2, *p = head; while(p!=NULL){ if(p->val<x){ t1->next = new ListNode(p->val); t1 = t1->next; } else{ t2->next = new ListNode(p->val); t2 = t2->next; } p=p->next; } t1->next=h2->next; return h1->next; } };