public boolean canPermutePalindrome(String s) {
   // Time: O(n), Space: O(n)
   if (s == null || s.length() == 0) {
     return true;
   }
   HashMap<Character, Integer> map = new HashMap<Character, Integer>();
   for (int i = 0; i < s.length(); i++) {
     char c = s.charAt(i);
     if (!map.containsKey(c)) {
       map.put(c, 1);
     } else {
       map.put(c, map.get(c) + 1);
     }
   }
   boolean single = false;
   for (Integer i : map.values()) {
     if (i % 2 == 1) {
       if (single) {
         return false;
       }
       single = true;
     }
   }
   return true;
 }
 public int maxPoints(Point[] points) {
   if (points == null || points.length == 0) {
     return 0;
   }
   HashMap<Double, Integer> map = new HashMap<>();
   int res = 0;
   for (int i = 0; i < points.length; i++) {
     int vertical = 0;
     int dup = 1;
     for (int j = i + 1; j < points.length; j++) {
       if (points[i].x == points[j].x) {
         if (points[i].y == points[j].y) {
           dup++;
         } else {
           vertical++;
         }
       } else {
         double slope;
         if (points[j].y == points[i].y) {
           slope = 0.0;
         } else {
           slope = (1.0) * (points[j].y - points[i].y) / (points[j].x - points[i].x);
         }
         if (!map.containsKey(slope)) {
           map.put(slope, 1);
         } else {
           map.put(slope, map.get(slope) + 1);
         }
       }
     }
     for (Integer val : map.values()) {
       res = Math.max(res, dup + val);
     }
     res = Math.max(res, dup + vertical);
     map.clear();
   }
   return res;
 }
  public List<Integer> findSubstring(String s, String[] words) {
    // alg:
    // extra HashMap to count remaining word in words.
    // use two pointers to create a sliding window of length == len(word) in words.
    // check in HashMap if words element is found, and update the counter
    // if a word in current sliding window is not in HashMap.keys, or if a counter is < 0,
    // HashMap back to the original and window moves a step a head.

    // corner case:
    // null, empty, s shorter then the total length of words
    if (s == null || words == null) {
      return null;
    }

    int sl = s.length();
    int wn = words.length;

    if (sl == 0 || wn == 0) {
      return null;
    }

    int wl = words[0].length();
    if (sl < wn * wl) {
      return null;
    }

    // general case:
    HashMap<String, int[]> counter = new HashMap<String, int[]>();
    for (String w : words) {
      if (!counter.containsKey(w)) {
        counter.put(
            w,
            new int[] {
              1, 1
            }); // second element in value array is the back up of original counter record to
        // recover.
      } else {
        counter.get(w)[0] = counter.get(w)[0] + 1;
        counter.get(w)[1] = counter.get(w)[0];
      }
    }

    int p1 = 0;
    int p2 = p1 + wl - 1; // sliding window.
    int remain = wn; // remain words to find in words
    List<Integer> rst = new ArrayList<Integer>();
    int index = p1;
    String curr = "";
    while (p2 < sl) {
      curr = s.substring(p1, p2 + 1);
      if (counter.containsKey(curr) == false) {
        for (int[] val : counter.values()) {
          val[0] = val[1];
        }
        p1 = index + 1;
        p2 = p1 + wl - 1;
        remain = wn;
        index = p1;
        continue;
      }

      counter.get(curr)[0] = counter.get(curr)[0] - 1;
      if (counter.get(curr)[0] < 0) {
        for (int[] val : counter.values()) {
          val[0] = val[1];
        }
        p1 = index + 1;
        p2 = p1 + wl - 1;
        remain = wn;
        index = p1;
        continue;
      }
      remain--;
      p2 += wl;
      p1 += wl;
      if (remain == 0) {
        rst.add(index);
        for (int[] val : counter.values()) {
          val[0] = val[1];
        }
        p1 = index + 1;
        p2 = p1 + wl - 1;
        remain = wn;
        index = p1;
      }
    }

    return rst;
  }