代码随想录训练营第九天 | 28. 找出字符串中第一个匹配项的下标、459. 重复的子字符串

131 阅读3分钟

28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

想都不要想,想不出来的。

3. 看完代码随想录之后的想法

关键在于构建前缀表。

  1. 初始化一个前缀head_str末尾指针j
  2. for循环一个模式串字串末尾位置遍历,其中i表示字串末尾,也即尾缀tail_str末尾:
    1. 如果head_str[j]!=tail_str[i],就要一直往前退。例如"aabaaf"中,如果i=4指向的倒数第一个'a',则j=1,现在i要指向'f'时,则j=j+1后指向'b'。两者不相等,j就要一直向后退,退到template[:j+1]能够出现在template[?~i+1]中。 实际上就是前缀表table[:j+1]现在充当了用template[:j+1]去匹配template[?~i+1]字串的前缀表,且必须从匹配上后面的连续字符,而非这个字串的任意位置。
    2. 如果head_str[j]==tail_str[i],就要推进j
    3. 然后将j记录到当前的前缀表table[i]这里要注意前缀表i号单元存的就是template[:i+1]的最长相等前后缀长度。

4. 实现过程中遇到的困难

class Solution: 
    def build_table(self, template: str) -> list: 
        j = 0 t_list = list(template) 
        table = [j] * len(t_list) 
        for i in range(1, len(t_list)): 
            while (j > 0) and (t_list[j] != t_list[i]): # 一定要大于0,不然table[j-1]没办法取。 
                j = table[j - 1] 
            if t_list[j] == t_list[i]: 
                j += 1 
            table[i] = j 
        return table 
        
    def strStr(self, haystack: str, needle: str) -> int: 
        table = self.build_table(needle) 
        haystack_list = list(haystack) 
        needle_list = list(needle) 
        j = 0 
        for i in range(0, len(haystack_list)): 
            while (j > 0) and (haystack_list[i] != needle_list[j]): 
                j = table[j - 1] 
            if needle_list[j] == haystack_list[i]: 
                j += 1 
            if j == len(needle_list): 
                return i + 1 - len(needle_list) 
        return -1
  1. 我喜欢前缀表不统一减一的构建方法,这样直观,table[i]一定表示了包括i号位在内的整个模板串的子串的最长相等前后缀的长度。
  2. 如果j=0而非统一减一的j=-1,那么判断不相等后不断往回退的while必须加上j>0的条件,这样才能用j = table[j - 1]向前退。
  3. 构建前缀表时i应该从1开始,不然template[j]==template[i]就一直成立了。注意后缀的定义是不能包括第一个元素的。
  4. 在真正对文本串用模板串匹配时,for循环的i却要从0开始,因为这不是前缀去匹配后缀的构建前缀表过程,而是两个互不相交的串匹配了,得从0开始。

5.学习时长

2小时。

459. 重复的子字符串 - 力扣(LeetCode)

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目的第一想法

还是没有想法,除非一个一个试,试每一个字串能不能把整个文本串都匹配一遍。

3. 看完代码随想录之后的想法

在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串。 这个结论我看不到显式的证明方法,但是也举不出反例。

class Solution:
    def build_table(self, template: str) -> list:
        j = 0
        t_list = list(template)
        table = [0] * len(t_list)
        for i in range(1, len(t_list)):
            while (j > 0) and (template[j] != template[i]):
                j = table[j - 1]
            if template[j] == template[i]:
                j += 1
            table[i] = j
        return table
    def repeatedSubstringPattern(self, s: str) -> bool:
        table = self.build_table(template=s)
        repeat_len = len(list(s)) - table[-1]
        if repeat_len != len(list(s)) and len(list(s)) % repeat_len == 0:
            return True
        else:
            return False

4. 实现遇到的困难

注意一定要是前缀表的最后一个元素记录的长度,而非最大长度。

5. 学习时长

30分钟。