Unique Substrings in Wraparound String

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.

定义"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd...."为循环字符串,并给定字符串p,要求在p的所有非空子串中,找出出现在循环字符串中那些子串。

题目分析

这题有一个隐藏的重要条件,就是这个循环字符串。该循环字符串并不是任意字符串的循环体,而是规定了是"abcdefghijklmnopqrstuvwxyz"的循环体,里面所有字母都是由abcd的顺序前后相连的。所以其实题目的意思是,找出字符串p所有子串中,每个字母按照字母表顺序相连的子串。比如,如果p是xyzabcd,那像xyz或者xyzabcd或者bcd这些子串,它们的字母都是按照字母表顺序相连的(为了省略篇幅,未穷举完所有可能)。按照字母表顺序相连,即意味着前字符的ascii码比后字符小1,或者后字符比前字符小25(za的情况)。

动态规划

复杂度

时间 O(N) 空间 O(1)

思路分析

  1. 由于符合条件的子串中字符是顺序相连的,所以如果前两个字符组成的子串是符合条件的话,如果第三个字符和第二个字符也是相连的,那这三个字符组成的子串肯定也是符合条件的
  2. 也是由于符合条件的子串中字符是顺序相连的,那么这个子串的长度有多长,就有多少种以该子串最后一个字符为结尾的小子串。比如abcd这个长度为4的子串,以d结尾有4中可能:d, cd, bcd, abcd
  3. 由于题目只要求找出唯一的子串数量,那么以某一个字符为结尾的子串,无论改字符出现在哪,它所可能组成的子串都是一样的,所以我们只需要找到能组成最长子串的那个位置就行了。比abcdxybcd中,以d结尾的子串必定是abcd, bcd, cd, d,所以第二次遇到b,c,d时,由于所组成子串长度不及第一次出现时子串的最大长度,所以就不用考虑了
  4. 那么根据前三点,我们只要找出p中每个字符所能组成的唯一子串数量,再把它们相加,就是p中所有符合条件的子串数量了
class Solution:
    def findSubstringInWraproundString(self, p):
        """
        :type p: str
        :rtype: int
        """
        count = [0 for i in range(0, 26)]
        length = 1
        if len(p) > 0:
            count[ord(p[0]) - ord('a')] = 1 # 以第一个字符结尾的子串长度为1
        for index in range(1, len(p)): # 从第二个字符开始,看时候能和前一个字符相连
            isConsecutive = ord(p[index]) - ord(p[index - 1])  == 1 or ord(p[index - 1]) - ord(p[index])  == 25
            if isConsecutive: # 如果是顺序相连的,那么当前能组成的子串最大长度加1
                length += 1
            else:
                length = 1 # 如果这个顺序被断开了,那么当前能组成的子串最大长度重置为1
            ascii = ord(p[index]) - ord('a')
            count[ascii] = max(count[ascii], length) # 记录以当前字符结尾的子串能达到的最大长度
        return sum(count) # 每个字符的最大长度,都代表这个字符结尾的唯一子串的数量,相加既是所有唯一子串数量

ethannnli
858 声望360 粉丝