private void helper(final int[] candidates, final int start, final int target, final ArrayList<Integer> item, final ArrayList<ArrayList<Integer>> res) {
        if (target < 0) {
            return;
        }

        if (target == 0) {
            // add one combination
            res.add(new ArrayList<Integer>(item));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            // check repeated elements
            if (i > 0 && candidates[i] == candidates[i - 1]) {
                continue;
            }
            item.add(candidates[i]);
            // recursion(dfs)
            // i won't increase 1 here is because we can choose same number repeatedly
            helper(candidates, i, target - candidates[i], item, res);
            // remove the last element when poping out of recursion
            // so that item will be used for the next loop
            // or if we can't find the combination, we need to remove those elements
            item.remove(item.size() - 1);
        }
    }