Consider the string s
to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz", so s
will look like this: "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....".
Now we have another string p
. Your job is to find out how many unique non-empty substrings of p
are present in s
. In particular, your input is the string p
and you need to output the number of different non-empty substrings of p
in the string s
.
Note: p
consists of only lowercase English letters and the size of p might be over 10000.
Example 1:
Input: "a" Output: 1 Explanation: Only the substring "a" of string "a" is in the string s.
Example 2:
Input: "cac" Output: 2 Explanation: There are two substrings "a", "c" of string "cac" in the string s.
Example 3:
Input: "zab" Output: 6 Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s.
这一题才是名副其实的medium难度,感觉还需要动一下小脑筋,但是实现又不复杂。
首先,这一题需要明确两点:
- 尽管子串是无限长,但是事实上,子串的内容是已知的:也就是说从任意字符a-z开始,给定任意长度,都是它的子串。比如,从a开始,长度为3,abc是有效的子串;比如从x开始,长度为5,xyzab是有效的子串;
- 任何有效子串的子集任然是有效子串。比如,abcde是有效子串,那么它的子集abc,ab,cd......都是有效子串。
根据以上两点,于是我们在计算的时候,可以使用一个数组int[26]来存储找到的子串,如:int[1]=2,就代表当前我们已经找到了b开头,长度为2的子串,bc。另外,我们可以发现,事实上,对于一个字符开头的子串,它的最大长度,也就是以该字符开头的子串的数量,对于刚才那个例子,b开头的子串数量,就是2。因此,非常幸运,最后我们将整个数组求和,就可以得到总共找到的子串数量了。
接下来,在p中找到有效子串,也就比较简单了:
- 使用循环,从第一个字符开始遍历,只要后面的字符比当前的字符大1,就是有效子串,直到该子串结束,记录下该子串。
- 然后对子串中的子集进行处理,处理起来很简单,比如:当前子串是abcde,那么a开头长度为5,去找int[0],如果值小于5,就更新为5;b开头长度为4,找到int[1],如果小于4,就更新为4,以此类推。
- 然后继续遍历下一个有效子串,直到p结束。
代码如下,复杂度大约为O(N),实际时间大约大于2N:
public class Solution {
public int findSubstringInWraproundString(String p) {
if (p.length() == 0) {
return 0;
}
int[] nArrDigits = new int[26];
char[] cs = p.toCharArray();
int nIndex = 0;
int nStart = 0;
int nEnd = 0;
while (nIndex + 1 < cs.length) {
if (cs[nIndex + 1] == ((((cs[nIndex] + 1) - 'a') % 26) + 'a')) {
nEnd = nIndex + 1;
} else {
updateArray(nStart, nEnd, nArrDigits, cs);
nStart = nIndex + 1;
nEnd = nIndex + 1;
}
nIndex++;
}
// compute the last part
updateArray(nStart, nEnd, nArrDigits, cs);
int nSum = 0;
for (int i : nArrDigits) {
nSum += i;
}
return nSum;
}
private void updateArray(int nStart, int nEnd, int[] nArrDigits, char[] cs) {
for (int i = nStart; i <= nEnd; i++) {
if (nArrDigits[cs[i] - 'a'] < nEnd - i + 1) {
nArrDigits[cs[i] - 'a'] = nEnd - i + 1;
}
}
}
}