• leetcode刷题笔录7


    存储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;
          }
      };
    作者:一叶斋主人
    出处:www.cnblogs.com/yiyezhai
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    数据挖掘实践(23):实战-- 建筑能源得分预测报告(一)
    返回闭包
    函数指针
    Rust 中的 Closure
    Moves, copies and clones in Rust
    Rust的闭包类型(Fn, FnMut, FnOne的区别)
    Clone VS Copy
    rust socket
    A simple UNIX socket listener in Rust
    【firecracker】系统启动与epoll事件循环
  • 原文地址:https://www.cnblogs.com/yiyezhai/p/3056748.html
Copyright © 2020-2023  润新知