代码随想录day8|28实现strSTR459重复的子字符串|01笔记

130 阅读3分钟
  • 28实现strSTR

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 暴力的方法是遍历haystack,每遇到needle的开头就进行一次比对,复杂度为O(n^2)
  • 讲解中关于KMP的重要点

  • 前缀表:记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
  • 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串
  • 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串
  • 匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面重新匹配就可以了。
  • 前缀表中模式串与前缀表对应位置的数字表示的是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀
  • 解题代码

  • 前缀表使用-1实现
  •     / 方法一:前缀表使用减1实现
        
        // getNext 构造前缀表next
        // params:
        //		  next 前缀表数组
        //		  s 模式串
        func getNext(next []int, s string) {
        	j := -1         // j表示 最长相等前后缀长度
        	next[0] = j
        
        	for i := 1; i < len(s); i++ {
        		for j >= 0 && s[i] != s[j+1] {
        			j = next[j]   // 回退前一位
        		}
        		if s[i] == s[j+1] {
        			j++
        		}
        		next[i] = j    // next[i]是i(包括i)之前的最长相等前后缀长度
        	}
        }
        func strStr(haystack string, needle string) int {
        	if len(needle) == 0 {
        		return 0
        	}
        	next := make([]int, len(needle))
        	getNext(next, needle)
        	j := -1            // 模式串的起始位置 next为-1 因此也为-1
        	for i := 0; i < len(haystack); i++ {
        		for j >= 0 && haystack[i] != needle[j+1] {
        			j = next[j]     // 寻找下一个匹配点
        		}
        		if haystack[i] == needle[j+1] {
        			j++
        		}
        		if j == len(needle)-1 {      // j指向了模式串的末尾
        			return i - len(needle) + 1
        		}
        	}
        	return -1
        }
    
  • 前缀表直接实现
  •     // 方法二: 前缀表无减一或者右移
        
        // getNext 构造前缀表next
        // params:
        //		  next 前缀表数组
        //		  s 模式串
        func getNext(next []int, s string) {
        	j := 0
        	next[0] = j
        	for i := 1; i < len(s); i++ {
        		for j > 0 && s[i] != s[j] {
        			j = next[j-1]
        		}
        		if s[i] == s[j] {
        			j++
        		}
        		next[i] = j
        	}
        }
        func strStr(haystack string, needle string) int {
        	n := len(needle)
        	if n == 0 {
        		return 0
        	}
        	j := 0
        	next := make([]int, n)
        	getNext(next, needle)
        	for i := 0; i < len(haystack); i++ {
        		for j > 0 && haystack[i] != needle[j] {
        			j = next[j-1]      // 回退到j的前一位
        		}
        		if haystack[i] == needle[j] {
        			j++
        		}
        		if j == n {
        			return i - n + 1
        		}
        	}
        	return -1
        }
    
  • 459重复的子字符串

  • 代码随想录 (programmercarl.com)
  • 解题思路

  • (一)如果字符串可以由其中包含的一个子字符串重复得到。那么将原本的字符串复制一份连接到本身的后面,那么新得到的二倍长度的字符串内部一定至少有一个不同于前一半和后一半的和原字符串相同的部分。
  • (二)也可以利用KMP算法求字符串的next数组,如果next最后的值大于0,且其与数组长度的差可以被原字符串长度整除,那么成立。
  • 解题代码

  • 不减一
  •     func repeatedSubstringPattern(s string) bool {
            len := len(s)
            if len < 2 {
                return false
            }
            next := make([]int,len)
            getNext(next, s)
            if next[len-1]!=0 && len%(len - next[len-1])==0 {
                return true
            }
        
            return false
        
        }
        
        func getNext(next []int, s string) {
            j := 0
            next[0] = j
            for i:=1;i<len(s);i++ {
                for j>0 && s[i] != s[j] {
                    j = next[j-1]
                }
                if s[i] == s[j] {
                    j++
                }
                next[i] = j
            }
        }
    
  • 减一
  •     func repeatedSubstringPattern(s string) bool {
        	n := len(s)
        	if n == 0 {
        		return false
        	}
        	next := make([]int, n)
        	j := -1
        	next[0] = j
        	for i := 1; i < n; i++ {
        		for j >= 0 && s[i] != s[j+1] {
        			j = next[j]
        		}
        		if s[i] == s[j+1] {
        			j++
        		}
        		next[i] = j
        	}
        	// next[n-1]+1 最长相同前后缀的长度
        	if next[n-1] != -1 && n%(n-(next[n-1]+1)) == 0 {
        		return true
        	}
        	return false
        }