首页 > 其他分享 >2024-01-17:lc的30. 串联所有单词的子串

2024-01-17:lc的30. 串联所有单词的子串

时间:2024-01-17 20:37:43浏览次数:23  
标签:01 hash 哈希 17 int64 str words ans lc

2024-01-17:用go语言,给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。

例如,如果 words = ["ab","cd","ef"],

那么 "abcdef", "abefcd","cdabef",

"cdefab","efabcd", 和 "efcdab" 都是串联子串,

"acdbef" 不是串联子串,因为他不是任何 words 排列的连接。

返回所有串联字串在 s 中的开始索引。

你可以以 任意顺序 返回答案。

1 <= s.length <= 10^4,

1 <= words.length <= 5000,

1 <= words[i].length <= 30。

words[i] 和 s 由小写英文字母组成。

输入:s = "barfoothefoobarman", words = ["foo","bar"]。

输出:[0,9]。

来自lc的30. 串联所有单词的子串。

答案2024-01-17:

来自左程云

灵捷3.5

大体过程如下:

  1. 定义一些常量和变量,包括 BASEMAXN,以及存储结果的切片 ans

  2. 实现 hashValue 函数,用于计算字符串的哈希值。这里使用一个基于索引的简单哈希函数将字符串映射为一个唯一的整数。

  3. 实现 buildHash 函数,用于构建字符串的前缀哈希数组。通过动态规划的方式计算每个位置的哈希值。

  4. 实现 hashValueRange 函数,用于计算子串的哈希值。利用前缀哈希数组,根据子串的起始和结束位置计算哈希值。

  5. 创建一个哈希表 mapCount 用于存储 words 中每个单词的出现次数。

  6. 构建字符串 s 的前缀哈希数组 hash

  7. 创建一个数组 pow,用于存储 BASE 的幂次方,便于后续计算子串的哈希值。

  8. 创建一个滑动窗口 window,用于记录当前窗口中每个单词出现的次数。

  9. 循环遍历 s 中每个起始位置的可能性(即从 0 到 wordLen-1)。

  10. 在每个起始位置,初始化一个变量 debt 用于记录还需要凑齐的单词数。

  11. 在每个起始位置,遍历 words 中的单词,依次将其添加到窗口中,并更新 debt 的值。

  12. 如果 debt 等于 0,表示窗口中已经包含了所有 words 中的单词,则将当前起始位置加入结果数组 ans 中。

  13. 对于每个起始位置,向右移动窗口,同时更新窗口中单词的出现次数。

  14. 检查窗口中的哈希值和单词出现次数是否符合要求,如果符合则将当前起始位置加入结果数组 ans 中。

  15. 清空滑动窗口 window

  16. 返回结果数组 ans

总的时间复杂度:O(n * m * k),其中 n 是字符串 s 的长度,m 是 words 的长度,k 是单词的平均长度。

总的额外空间复杂度:O(n),其中 n 是字符串 s 的长度,主要用于存储哈希表、前缀哈希数组和结果数组。

go完整代码如下:

package main

import (
	"fmt"
)

var BASE = 499
var MAXN = 10001

func hashValue(str string) int64 {
	if str == "" {
		return 0
	}
	n := len(str)
	ans := int64(str[0]-'a') + 1
	for j := 1; j < n; j++ {
		ans = ans*int64(BASE) + int64(str[j]-'a') + 1
	}
	return ans
}

func buildHash(str string) []int64 {
	hash := make([]int64, len(str))
	hash[0] = int64(str[0]-'a') + 1
	for j := 1; j < len(str); j++ {
		hash[j] = hash[j-1]*int64(BASE) + int64(str[j]-'a') + 1
	}
	return hash
}

func hashValueRange(l, r int, hash []int64, pow []int64) int64 {
	ans := hash[r-1]
	if l > 0 {
		ans -= hash[l-1] * pow[r-l]
	}
	return ans
}

func findSubstring(s string, words []string) []int {
	var ans []int
	if len(s) == 0 || len(words) == 0 {
		return ans
	}

	wordLen := len(words[0])
	wordNum := len(words)
	allLen := wordLen * wordNum

	mapCount := make(map[int64]int)
	for _, key := range words {
		v := hashValue(key)
		mapCount[v]++
	}

	hash := buildHash(s)
	pow := make([]int64, MAXN)
	pow[0] = 1
	for j := 1; j < MAXN; j++ {
		pow[j] = pow[j-1] * int64(BASE)
	}

	window := make(map[int64]int)

	for init := 0; init < wordLen && init+allLen <= len(s); init++ {
		debt := wordNum
		for l, r, part := init, init+wordLen, 0; part < wordNum; l += wordLen {
			cur := hashValueRange(l, r, hash, pow)
			window[cur]++
			if window[cur] <= mapCount[cur] {
				debt--
			}
			r += wordLen
			part++
		}
		if debt == 0 {
			ans = append(ans, init)
		}

		for l1, r1, l2, r2 := init, init+wordLen, init+allLen, init+allLen+wordLen; r2 <= len(s); l1, r1, l2, r2 = l1+wordLen, r1+wordLen, l2+wordLen, r2+wordLen {
			out := hashValueRange(l1, r1, hash, pow)
			in := hashValueRange(l2, r2, hash, pow)
			window[out]--
			if window[out] < mapCount[out] {
				debt++
			}
			window[in]++
			if window[in] <= mapCount[in] {
				debt--
			}
			if debt == 0 {
				ans = append(ans, r1)
			}
		}

		for key := range window {
			delete(window, key)
		}
	}

	return ans
}

func main() {
	s := "barfoothefoobarman"
	words := []string{"foo", "bar"}

	result := findSubstring(s, words)

	fmt.Println(result)
}

在这里插入图片描述

标签:01,hash,哈希,17,int64,str,words,ans,lc
From: https://www.cnblogs.com/moonfdd/p/17971110

相关文章

  • 数据分析01
    数据分析-01http.request.method==POST筛选出采用http协议的post方式的数据包,发现请求都是172.16.1.110,发送到172.16.1.18黑客IP是172.16.1.110或者使⽤过滤规则tcp.connection.syn过滤出所有带有SYN标志位的数据包,发现IP为172.16.1.110的易受攻击端⼝短时间内发送了⼤......
  • 0117
    不想加密码了,就这样(躺)关于一些抽象的事实早上醒了但是没起来,就直接让我爸送万达去了。至少没有出门两个小时,还是很感动的。光荣的在万达走丢了。密室门开了我都不知道门开了他也不知道虽然因为门是拉的。然后门开了我俩都不知道。恐怖密室真的会有npc抓脚踝。。太恐......
  • P7424 [THUPC2017] 天天爱射击
    [THUPC2017]天天爱射击题目描述小C爱上了一款名字叫做《天天爱射击》的游戏。如图所示,这个游戏有一些平行于\(x\)轴的木板。现在有一些子弹,按顺序沿着\(y\)轴方向向这些木板射去。第\(i\)块木板被\(S_i\)个子弹贯穿以后,就会碎掉消失。一个子弹可以贯穿其弹道上的全部......
  • Doubly linked list【1月17日学习笔记】
    点击查看代码//Doublylinkedlist#include<iostream>usingnamespacestd;structnode{ intdata; node*next; node*prev;};//定义双向链表结构体node*A;node*getnewnode(intx){ node*temp=newnode; temp->data=x; temp->prev=NULL; temp->nex......
  • 01分数规划
    01分数规划有\(n\)个物品,每个物品有两个权值\(a_{i}\),\(b_{i}\),现在要去掉\(k\)个物品,使得剩下的\(n-k\)个物品\(\frac{\suma_{i}}{\sumb_{i}}\)有最大值,并求出该最大值。\(\frac{\suma_{i}}{\sumb_{i}}\)=\(\frac{\suma_{i}*w_{i}}{\sumb_{i}*w_{i}}\)(\(w_......
  • noip2015 跳石头
    原题链接根据最近的刷题经验,这种求最大最小值问题都是二分答案。首先,我们确定面对一个k,如果它符合题意,那么比他小的值也符合,如果他不符合题意,那么比他大的值更不符合;那么我们要求的就是符合找出11110000中最右边边的1。接着,我们该如何判断k是否符合题意呢?显而易见,从起点遍历所......
  • Solution Set【2024.1.17】
    [ABC298Ex]SumofMinofLength在下文的推导中假设\(\operatorname{depth}_{L}\le\operatorname{depth}_R\),若不符合则交换\(L\)和\(R\)。首先我们可以发现,我们可以找到\(R\)的\(\left\lfloor\frac{\operatorname{dist}\left(L,R\right)}{2}\right\rfloor\)级祖先......
  • 2024年3月17日DAMA-CDGP数据治理专家认证考试开始报名
    DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业竞争能力。DAMA是数据管理方面的认证,帮助数据从业者提升......
  • 洛谷题单指南-模拟和高精度-P1563 [NOIP2016 提高组] 玩具谜题
    原题链接:https://www.luogu.com.cn/problem/P1563题意解读:本题关键在于根据小人的朝向和寻找的方向来确定数组下标的变化。用数组存储小人,intd[]存朝向,inta[]存名称,朝向和寻找方向有4种组合:朝向(0:向内,1:向外)  寻找方向(0:左,1:右)  数组下标操作00顺时针寻找,下标递减......
  • ETLCloud详解,如何实现最佳实践及问题排查
    ETLCloud介绍ETLCloud是新一代全域数据集成平台,领先于市场同类产品的数据集成平台(DataOps),只需单击几下即可完成数据清洗转换、传输入仓等操作,具备高效、智能、一站式的全域数据集成优势,如:毫秒级实时数据同步支持异构数据源实时数据监听读取,实时数据通过经过清选、转换后可以实时......