https://leetcode.com/problems/unique-letter-string/description/
A character is unique in string S
if it occurs exactly once in it.
For example, in string S = "LETTER"
, the only unique characters are "L"
and "R"
.
Let's define UNIQ(S)
as the number of unique characters in string S
.
For example, UNIQ("LETTER") = 2
.
Given a string S
with only uppercases, calculate the sum of UNIQ(substring)
over all non-empty substrings of S
.
If there are two or more equal substrings at different positions in S
, we consider them different.
Since the answer can be very large, return the answer modulo 10 ^ 9 + 7
.
Example 1:
Input: "ABC"
Output: 10
Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC".
Evey substring is composed with only unique letters.
Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10
Example 2:
Input: "ABA"
Output: 8
Explanation: The same as example 1, except uni("ABA") = 1.
分析
看了discuss,大神的思维和我等常人就是不一样,跪服。首先以字符串XAXAXXAX为例,如果将第二个 A 变成唯一的character的话,只可能时某个唯一的substring包含了这个A。比如:
We can take "XA(XAXX)AX"
and between "()"
is our substring.
利用这种方式,可以使得第二个A成为唯一的character,那么从上可知我们要做的是:
We can see here, to make the second "A" counted as a uniq character, we need to:
- insert
"("
somewhere between the first and secondA
- insert
")"
somewhere between the second and thirdA
For step 1 we have "A(XA"
and "AX(A"
, 2 possibility.
For step 2 we have "A)XXA"
, "AX)XA"
and "AXX)A"
, 3 possibilities.
So there are in total 2 * 3 = 6
ways to make the second A
a unique character in a substring.
In other words, there are only 6 substring, in which this A
contribute 1 point as unique string.
现在逆转下思维,一开始的思维是在原字符串的所有子串中寻找可能的唯一的character,我们现在就直接在S中来计算每个字符,看看对于每个unique char总公有多少种不同的找法。换而言之就是,对于S中的每个字符,如果他作为unique char的话,那么包含他的substring的范围是在前一个相同字符以及后一个相同字符这个区间内找的(参见上面的A),将所有可能的substring数量加起来即可,很有趣的逆向思维。
Explanation:
-
index[26][2]
record last two occurrence index for every upper characters. - Initialise all values in
index
to-1
. - Loop on string S, for every character
c
, update its last two occurrence index toindex[c]
. - Count when loop. For example, if "A" appears twice at index 3, 6, 9 seperately, we need to count:
- For the first "A": (6-3) * (3-(-1))"
- For the second "A": (9-6) * (6-3)"
- For the third "A": (N-9) * (9-6)"
代码
public int uniqueLetterString(String S) {
int[][] index = new int[26][2];
for (int i = 0; i < 26; ++i) Arrays.fill(index[i], -1);
long res = 0, N = S.length(), mod = (int) Math.pow(10, 9) + 7;
for (int i = 0; i < N; i++) {
int c = S.charAt(i) - 'A';
res = res + (i - index[c][1]) * (index[c][1] - index[c][0]);
index[c] = new int[]{index[c][1], i};
}
// 计算最后的c
for (int c = 0; c < 26; ++c)
res = res + (N - index[c][1]) * (index[c][1] - index[c][0]);
return (int) (res % mod);
}