// This way will also work, just a little bit more work by encoding each string into the same one
    // Kinda similar to the isomorphic string
	public boolean wordPatternEncoding(String pattern, String str) {
		if (str == null || str.isEmpty() || pattern == null || pattern.isEmpty()) {
			return false;
		}

		String[] s = str.split(" ");
		if (pattern.length() != s.length) {
			return false;
		}

		// encode pattern
		String patternEncoded = this.encodeString(pattern);
		// encode the string array
		String strEncoded = this.encodeArray(s);

		// compare
		return patternEncoded.equals(strEncoded);
	}

	private String encodeArray(String[] s) {
		Map<String, Character> lookup = new HashMap<>();

		int index = 0; // starting from 'a'
		StringBuilder sb = new StringBuilder();
		for (String ss : s) {
			if (lookup.containsKey(ss)) {
				sb.append(lookup.get(ss));
			} else {
				char c = (char)('a' + index);
				sb.append(c);
				index++;
				lookup.put(ss, c);
			}
		}
		return sb.toString();
	}

	// encode it to base to a, this is not really encoding, but mapping a char to a completely different one using
	// the same order as encodeArray
	private String encodeString(String s) {
		Map<Character, Character> lookup = new HashMap<>();
		int index = 0; // starting from 'a'
		StringBuilder sb = new StringBuilder();
		
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			
			if (lookup.containsKey(c)) {
				sb.append(lookup.get(c));
			} else {
				char t = (char)('a' + index);
				sb.append(t);
				index++;
				lookup.put(c,  t);
			}
		}
		
		return sb.toString();
	}