Java O(N) O(1) - DP (but not that hard to understand)
\npublic int uniqueLetterString(String s) {\n        if (s == null || s.length() == 0) {\n            return 0;\n        }\n        int[][] dp = new int[26][2];
```\npublic int uniqueLetterString(String s) {\n        if (s == null || s.length() == 0) {\n            return 0;\n        }\n        int[][] dp = new int[26][2];\n    \n        for (int i = 0; i < 26; i ++) {\n            Arrays.fill(dp[i], -1);\n        }\n\t\t// using bottom up DP approach\n        // dp[charIndex][0] representing value from previous position\n        // dp[charIndex][1] representing value from current position\n        int res = 0;\n        for (int i = 0; i < s.length(); i ++) {\n            int charIndex = s.charAt(i) - \'A\';\n            dp[charIndex][0] = dp[charIndex][1];\n            dp[charIndex][1] = i;       \n            // dynamically calculating increase from last position to current position for all chars\n            // for example,\n            // for "A",  dp value dp[0][0] = -1; dp[0][1] = 0\n            // for "AB", dp value will be: \n            //      dp[0][0]: -1 ,  dp[0][1]: 0 (from last position)\n            //      dp[1][0]: -1, dp[1][1] = 1;\n            // for "AA" dp value would be:\n            //      dp[0][0]: 0 (copy from dp[0][1] of last position),  dp[0][1]: 1\n            int tmp = 0;\n            for (int j = 0; j < 26; j ++) {               \n                tmp += (dp[j][1] - dp[j][0]);                 \n            }\n            res += tmp;         \n        }\n        return res;\n    }    \n}\n\n// Time O(N) and Space O(1) (only 26 cuppercase English letters)\n```
[]