// O(n^2) solution
  public static int checkAns(ArrayList<String> A, ArrayList<String> Q) {
    HashSet<String> dict = new HashSet<String>();
    for (String s : Q) {
      dict.add(s);
    }

    Pair<Integer, Integer> ans = new Pair<Integer, Integer>(0, A.size() - 1);
    for (int l = 0; l < A.size(); ++l) {
      HashMap<String, Integer> count = new HashMap<String, Integer>();
      for (int r = l; r < A.size() && r - l < ans.getSecond() - ans.getFirst(); ++r) {
        if (dict.contains(A.get(r))) {
          count.put(A.get(r), count.containsKey(A.get(r)) ? count.get(A.get(r)) + 1 : 1);
        }
        if (count.size() == Q.size()) {
          if (r - l < ans.getSecond() - ans.getFirst()) {
            ans.setFirst(l);
            ans.setSecond(r);
          }
          break;
        }
      }
      count.clear();
    }

    return ans.getSecond() - ans.getFirst();
  }
  static void checkAns(
      int[][] professor_preference,
      int[][] student_preference,
      Pair<Integer, Integer>[] matchResult) {

    assert matchResult.length == professor_preference.length;

    boolean[] professor = new boolean[professor_preference.length];
    boolean[] student = new boolean[student_preference.length];

    for (Pair<Integer, Integer> p : matchResult) {
      student[p.getFirst()] = true;
      professor[p.getSecond()] = true;
    }
    for (boolean p : professor) {
      assert p;
    }
    for (boolean s : student) {
      assert s;
    }

    for (int i = 0; i < matchResult.length; ++i) {
      for (int j = i + 1; j < matchResult.length; ++j) {
        int s0 = matchResult[i].getFirst(), a0 = matchResult[i].getSecond();
        int s1 = matchResult[j].getFirst(), a1 = matchResult[j].getSecond();
        int a0InS0Order = find(student_preference[s0], a0);
        int a1InS0Order = find(student_preference[s0], a1);
        int s0InA1Order = find(professor_preference[a1], s0);
        int s1InA1Order = find(professor_preference[a1], s1);
        assert a0InS0Order < a1InS0Order || s1InA1Order < s0InA1Order;
      }
    }
  }
  // @include
  public static Pair<Integer, Integer> findSmallestSubarrayCoveringSubset(
      ArrayList<String> A, ArrayList<String> Q) {
    HashSet<String> dict = new HashSet<String>(Q);
    HashMap<String, Integer> countQ = new HashMap<String, Integer>();
    int l = 0, r = 0;
    Pair<Integer, Integer> res = new Pair<Integer, Integer>(-1, -1);
    while (r < A.size()) {
      // Keep moving r until it reaches end or countQ has |Q| items.
      while (r < A.size() && countQ.size() < Q.size()) {
        if (dict.contains(A.get(r))) {
          countQ.put(A.get(r), countQ.containsKey(A.get(r)) ? countQ.get(A.get(r)) + 1 : 1);
        }
        ++r;
      }

      if (countQ.size() == Q.size()
          && // found |Q| keywords.
          ((res.getFirst() == -1 && res.getSecond() == -1)
              || r - 1 - l < res.getSecond() - res.getFirst())) {
        res.setFirst(l);
        res.setSecond(r - 1);
      }

      // Keep moving l until it reaches end or countQ has less |Q| items.
      while (l < r && countQ.size() == Q.size()) {
        if (dict.contains(A.get(l))) {
          int it = countQ.get(A.get(l));
          countQ.put(A.get(l), --it);
          if (it == 0) {
            countQ.remove(A.get(l));
            if ((res.getFirst() == -1 && res.getSecond() == -1)
                || r - 1 - l < res.getSecond() - res.getFirst()) {
              res.setFirst(l);
              res.setSecond(r - 1);
            }
          }
        }
        ++l;
      }
    }
    return res;
  }
  public static void main(String[] args) {
    Random r = new Random();
    for (int times = 0; times < 1000; ++times) {
      int n;
      if (args.length == 1) {
        n = Integer.parseInt(args[0]);
      } else {
        n = r.nextInt(10001);
      }
      List<Integer> A = new ArrayList<Integer>();
      for (int i = 0; i < n; ++i) {
        A.add(r.nextInt(n + 1));
      }

      assert (findLongestContainedRangeInt(A) == checkAns(A));
      Pair<Integer, Integer> res = findLongestContainedRange(A);
      System.out.println(res);
      assert (res.getSecond() - res.getFirst() + 1 == findLongestContainedRangeInt(A));
    }
  }
  public static void main(String[] args) {
    Random gen = new Random();
    for (int times = 0; times < 1000; ++times) {
      int n;
      ArrayList<String> A = new ArrayList<String>();
      if (args.length == 1) {
        n = Integer.parseInt(args[0]);
      } else {
        n = gen.nextInt(10000) + 1;
      }
      for (int i = 0; i < n; ++i) {
        A.add(randString(gen.nextInt(10) + 1));
      }
      /*
       * for (int i = 0; i < A.size(); ++i) { cout << A[i] << ' '; } cout <<
       * endl;
       */
      HashSet<String> dict = new HashSet<String>(A);
      int m = gen.nextInt(dict.size()) + 1;
      ArrayList<String> Q = new ArrayList<String>();
      Iterator<String> it = dict.iterator();
      while (it.hasNext()) {
        Q.add(it.next());
        if (--m == 0) {
          break;
        }
      }

      /*
       * for (int i = 0; i < Q.size(); ++i) { cout << Q[i] << ' '; } cout <<
       * endl;
       */
      Pair<Integer, Integer> res = findSmallestSubarrayCoveringSubset(A, Q);
      System.out.println(res.getFirst() + ", " + res.getSecond());
      dict.clear();
      for (int i = 0; i < Q.size(); ++i) {
        dict.add(Q.get(i));
      }
      for (int i = res.getFirst(); i <= res.getSecond(); ++i) {
        if (dict.contains(A.get(i))) {
          dict.remove(A.get(i));
        }
      }
      assert (dict.isEmpty() == true);
      Pair<Integer, Integer> res2 =
          SmallestSubarrayCoveringSetStream.findSmallestSubarrayCoveringSubset(A, Q);
      System.out.println(res2.getFirst() + ", " + res2.getSecond());
      dict.clear();
      for (int i = 0; i < Q.size(); ++i) {
        dict.add(Q.get(i));
      }
      for (int i = res.getFirst(); i <= res.getSecond(); ++i) {
        if (dict.contains(A.get(i))) {
          dict.remove(A.get(i));
        }
      }
      assert (dict.isEmpty() == true);
      assert (res.getSecond() - res.getFirst() == res2.getSecond() - res2.getFirst());
      assert (res.getSecond() - res.getFirst() == checkAns(A, Q));
    }
  }